﻿--[[****************************************************************
	DuckieBank v2.5b

	Author: Passion Development - www.sistersofpassion.org
	****************************************************************
	Description:
		Type /db or /duckiebank for brief help

	Official Development Site:
		http://duckiebank.googlecode.com

	Passion Development Team:
		Project Manager: Kaylley (Sentinals/Sister Of Passion)
		
		Developers: 
		AuroraNova, Glowusbirdus, Luzy (Sentinals/Sisters Of Passion)
		
		UI / Graphics: 
		
		Support Staff:
		
		Special Assistnace:
		Evil Duck (Original developer)
	
	
	Credits:

        Thanks to Evil Duck for writing this addon and supporting it upto version 2.41a - and all his assitnce since! 
        
        Thanks to wow.jaslaughter.com for the great BankItems addon
		that really helped this project get started.
		Additional thanks to:
			Yatlas and Gello
			wowwiki.com
			lua-users.org

License Information:

This software falls under the MIT License Model:

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 


	****************************************************************]]
-- 2.5b  Corrected nil value bug in Ducknet
-- 2.5a  Replaced missing images folder
-- 2.5   Base of 2.5 Development Stream
--       Added version to tooltip
--       Added option to open main bank frame with /db open command
--       Fixed a bug where the count of requests for DB:M managers could show as 0 if that person had never requested an item before
--       Added EULA Info
--		 Update documentation & FAQ
-- 2.41a Changed a timer for bank-snapshot
-- 2.41  Fixed rare bug that could happen after server patch and/or restart, workaround-test for when data can't be sent, added
--       timestamp to items, added sorting by level/type/subtype, better handling when switching guilds, added spanish locale
-- 2.40b Another LUA compatibility fixed after 2.1 patch, DuckieBank is smoother when adding/removing/moving in bank/bags
-- 2.40a Fixed incompatibility with WoW 2.1.0, bugfix in banker assignment through officer's note, fixed bug in guild roster display
-- 2.40  Strengthened test for uncached items, new request-window, added options-window, added banker-setting in officer's note,
--       changed WoW API call for rank-test (hope that works better with the servers out there), changed scan-scheme to better
--       work with bag-modifier addons, more agressive removal of non-bankers, faster tooltips for items (up to a point of max client-items),
--       separated realms, improved sorting and colour-coding of items, basic filtering implemented, mousewheel handler.
-- 2.36  Fixed bug in item-stacking (and consequently also request-system), added money, fixed reloadui issue
-- 2.34a Added a cleaning-function of incorrect bankers
-- 2.34  Added colour-coded frames, sorting of items, single-stacks, mouse-over request-list, bag selector, unstacked prices, server verbose
-- 2.32  A couple compatibility issues fixed with the new LUA engine in WoW 2.0
-- 2.31  Fixed bug in request clean-up
-- 2.30  Request-system, bugfix in banker removal, redesigned poll-system, other minor bugfixes
-- 2.20  Revamped UI, added cost-system, changed poll-timer scheme, note-bug fixed
-- 2.11  Fixed bug in INFO receive
-- 2.10  Added notes for bankers
-- 2.08  Bugfix for bankers who are not including their bags
-- 2.07  Minor fixes for banker storage
-- 2.06  Includes version when polling, picks up unknown bankers when seen
-- 2.05  Added send-button, moved banker date, 3 minutes holdoff timer, bankwindow can stay closed for bag update
-- 2.04  Some debug, and added checking of version
-- 2.03  Removed "normal" channels
-- 1.99  Extracted the network code to form the DuckNet addon
-- 1.51  New layout and multiple bankers test-version
-- 1.02  In-game protocol really coming along. First guild-wide test started.
-- 1.01  First move for in-game protocol
-- 0.10  Clean-up for first release
-- 0.02  Added minimap button
-- 0.01  Added display of saved date and time
-- 0.00  Most stuff is there


DUCKIEBANK_VERSIONTEXT = "DuckieBank v2.5b";
DUCKIEBANK_REQUIRED_DUCKNET = 1;
SLASH_DUCKIEBANK1 = "/duckiebank";
SLASH_DUCKIEBANK2 = "/db";

DUCKIEBANK_VERSION_DATA = "002";	-- The data structure in use
DUCKIEBANK_VERSION_BANKNUM = "0250";	-- The DuckieBank numeric string
DUCKIEBANK_PREFIX = "DuBa";
DUCKIEBANK_MARKER0 = "DuckieBank-Marker0";
DUCKIEBANK_MARKER1 = "DuckieBank-Marker1";
DUCKIEBANK_MARKER2 = "DuckieBank-Marker2";
DUCKIEBANK_MARKER3 = "DuckieBank-Marker3";
DUCKIEBANK_MARKER4 = "DuckieBank-Marker4";
DUCKIEBANK_MARKER5 = "DuckieBank-Marker5";
DUCKIEBANK_MARKER6 = "DuckieBank-Marker6";
DUCKIEBANK_MARKER7 = "DuckieBank-Marker7";
DUCKIEBANK_MARKER8 = "DuckieBank-Marker8";
DUCKIEBANK_MARKER9 = "DuckieBank-Marker9";
DUCKIEBANK_COLLATE = "DuckieBank-Collate";
DUCKIEBANK_REQUEST = "DuckieBank-Requests";
DUCKIEBANK_SETTING = DUCKIEBANK_MARKER0;

ITEMCOLORGREEN="m060"
ITEMCOLORYELLOW="m120"
ITEMCOLORORANGE="m150"
ITEMCOLORCYAN="p000"
ITEMCOLORBLUE="p060"
ITEMCOLORPURPLE="p090"
ITEMCOLORPINK="p120"
ITEMCOLORRED="p180"

DuckieBank_DB = { };

--[[	DB structure

guild 1
	1
		Banker 1
		Stamp
		Notes
		Settings
			Stamp = Stamp
			<Setting> = "Setting data"
			<Setting> = "Setting data"
	n
		Banker n
		Stamp
		Notes
		Settings
			...
	Banker 1
		1 { slot 1 }
		n { slot n }
	Banker n
		1 { slot 1 }
		n { slot n }
	Requests
		Banker 1
			Player 1
				Stamp
				1 { itemID, Count, Notes, Stamp [, Removed] }
				n { itemID, Count, Notes, Stamp [, Removed] }
			Player 2
				...
		Banker n
			...
				...
guild n
	...
]]

DuckieBank_LS = {
	IconPosition = 180,
	Debug=false,
	DebugServer=false,
	LastIncomingRequest = {},
	NotAgressive = nil,
};
local DuckieBank_LastGuild = nil;
local DuckieBank_LastGuildName = nil;
local DuckieBank_Banker = false;
local DuckieBank_Monitor = false;
local DuckieBank_VersionNotified = false;		-- User has (or has not) been notified about a new version if that exists
local DuckieBank_OldBankerNotes="";
local DuckieBank_DataLoaded=false;
local DuckieBank_DataCache=nil;
local DuckieBank_WrongDuckNet=nil;
local DuckieBank_CacheRetries=0;
local DuckieBank_SendDelayed=0;

local DuckieBank_Ready = false;
local DuckieBank_Transmitting = 0;
local DuckieBank_Trans = { };
local DuckieBank_BankerSend=nil;
local DuckieBank_EditItem=nil;
local DuckieBank_CurrentRequestTable=nil;
local DuckieBank_NewRequests=false;
local DuckieBank_CollateEvent="None";
local DuckieBank_NetHook=nil;
local DuckieBank_SnapshotTimer=0;

local DB_TYPE_BANKER   = nil;
local DB_TYPE_REQUEST  = 1;
local DB_TYPE_SETTINGS = 2;
local DuckieBank_Header = {
	VerData=0,			-- Data version
	VerSender=0,		-- Sender version
	VerBanker=0,		-- Banker version of the addon that made the original snapshot
	Banker=nil,			-- Currently receiving banker
	Stamp=0,			-- The accompanying stamp
	Notes="",			-- Banker's notes
	Player=nil,			-- Player name
	Type=nil;			-- No special type, so normal banker
};
local DuckieBank_TransmitPressed=nil;
local DuckieBank_Timers = {
	Last=0,					-- Running heartbeat timekeeper
	NextPoll=600,			-- Not important. Wont run yet anyway.
	NextSend=-1,			-- Not active
	StuffIt=-1,				-- Not active
	UpdateNotified=false,	-- Upcoming update not notified
	LastInStamp=0,			-- Last incoming data
	CollateBump=-1,
	BumpSendRequests=-1;
	BumpSendSettings=-1;
};
local DuckieBank_Poll = {
	Section="Requests",
	Index=0,
};
local DuckieBank_RL = {};
local DuckieBank_NewStuff={};
local DuckieBank_Loader = {
	index=1,
	entry=1,
};


DBT_INACTIVITY_NOTIFY = 10;			-- 10 seconds before broadcast will notify on upcoming update
DBT_INACTIVITY_DELAY = 2;			-- 2 seconds of event delay
DBT_INACTIVITY_SEND = 180;			-- 3 minutes inactivity in the bank and bags will send new contents
DBT_INTERVAL_POLL = 3600;			-- Not important. Unused at first.
DBT_INTERVAL_POLLREST = 1800;		-- You're done all, so rest a bit
DBT_INACTIVITY_BANKER = 1296000;	-- (60x60x24x15) 15 days old banker-data will be deleted
DBT_REQUEST_DEATH = 604800;			-- (60x60x24x7) 7 days
DBT_RESEND_DELAY = 5;				-- Delay when attempting to send again
DBT_RESEND_MAXIMUM = 120;			-- Max time for attempting resend before DuckNet reset
local DB_HEARTBEAT = 0.3;

local DB_BANKERRANK="banker";

local DBT_ARMOR=nil;
local DBT_CONSUMABLE=nil;
local DBT_CONTAINER=nil;
local DBT_REAGENT=nil;
local DBT_RECIPE=nil;
local DBT_PROJECTILE=nil;
local DBT_QUEST=nil;
local DBT_QUIVER=nil;
local DBT_TRADEGOODS=nil;
local DBT_WEAPON=nil;

local DBV_ABORT = 0;
local DBV_RETRY = 1;
local DBV_OKAY  = 2;

-- Set up for handling
function DuckieBank_OnLoad()
	DuckieBank_Ready=false;
	DuckieBank_LastGuild=nil;							-- Thanks to Baldmuffin for the heads-up on this one
	DuckieBank_DisplayNeedInit=true;
	this:RegisterEvent("VARIABLES_LOADED");
	this:RegisterEvent("BANKFRAME_OPENED");				-- Bank opened
	this:RegisterEvent("BANKFRAME_CLOSED");				-- Bank closed
	this:RegisterEvent("PLAYERBANKSLOTS_CHANGED");		-- Bank 28x changed
	this:RegisterEvent("PLAYERBANKBAGSLOTS_CHANGED");	-- Bank 7-bag changed
	this:RegisterEvent("BAG_UPDATE");					-- Player-bags updated
	this:RegisterEvent("GUILD_ROSTER_UPDATE");			-- Local guild-roster cache updated
	this:RegisterEvent("GUILD_INVITE_REQUEST");			-- Request to join a guild

	SlashCmdList["DUCKIEBANK"]=function(msg) DuckieBank_Slasher(msg) end;
	tinsert(UISpecialFrames,"DuckieBank_AFrame");
	tinsert(UISpecialFrames,"DuckieBank_BankerFrame");

	math.randomseed(time()); math.random(); math.random(); math.random();

	DuckieBank_SendBankerButton:Hide();
	DuckieBank_FillCostVBuy:Hide(); DuckieBank_FillCostVSell:Hide(); DuckieBank_ClearAllCosts:Hide();
	DuckieBank_EditCustomCost:Hide(); DuckieBank_OpenBankerButton:Hide();

	DuckieBank_RequestInfo:SetText(DUCKIEBANK_UITEXT_REQUESTITEM);
	DuckieBank_ABankerNotesHeader:SetText(DUCKIEBANK_UITEXT_BANKERNOTES);


--	DuckieBank_Chat("Loaded");
end


	-- Note that we're not using the accurate form. The reason is that this code
	-- does not need accurate timing, and this way has a much bigger chance of
	-- pausing if any hick-ups should occur elsewhere - thus increasing the chance
	-- of correct functionality.
-- The heartbeat
function DuckieBank_HeartBeat(elapsed)
	if (DuckieBank_NetHook) then DuckieBank_NetHook(elapsed); end	-- The network driver depends on infusion from me
	DuckieBank_Timers.Last=DuckieBank_Timers.Last+elapsed;			-- Update this
	if (DuckieBank_Timers.Last<DB_HEARTBEAT) then return; end;		-- Heartbeat is 500ms
	DuckieBank_Timers.Last=0;										-- Restart
	if (DuckieBank_Ready~=true) then DuckieBank_BootBank(); return; end

	if (DuckieBank_LastGuild and DuckieBank_Loader.index>0) then
		if (not DuckieBank_LastGuild[DuckieBank_Loader.index] or not DuckieBank_LastGuild[DuckieBank_Loader.index].Banker) then
			DuckieBank_Loader.index=0; DuckieBank_Loader.entry=1;
			if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Cache done"); end
			return;
		end;
		if (DuckieBank_LS.Debug and DuckieBank_Loader.index==1 and DuckieBank_Loader.entry==1) then DuckieBank_Chat("DEBUG: Running cache"); end
		local tp=DuckieBank_LastGuild[DuckieBank_LastGuild[DuckieBank_Loader.index].Banker];	-- Banker base root
		if (not tp) then DuckieBank_Loader.index=0; DuckieBank_Loader.entry=1; return; end;
		if (not tp[DuckieBank_Loader.entry]) then DuckieBank_Loader.index=DuckieBank_Loader.index+1; DuckieBank_Loader.entry=1; return; end;
		if (DuckieBank_CacheItem(tp[DuckieBank_Loader.entry].link)) then DuckieBank_Loader.entry=DuckieBank_Loader.entry+1; end
	end

	if (DuckieBank_Banker and DuckieBank_AFrame:IsVisible()) then
		if (DuckNet_CanTransmit(DUCKIEBANK_PREFIX)) then
			DuckieBank_AInfoText:SetText(DUCKIEBANK_VERSIONTEXT);
			DuckieBank_AInfoText:SetTextColor(1,1,1);
		else
			DuckieBank_AInfoText:SetText(DUCKIEBANK_TEXT_NETBUSY);
			DuckieBank_AInfoText:SetTextColor(1,1,0);
		end
	end

--[[		Poll-dispatcher		]]
	DuckieBank_Timers.NextPoll=DuckieBank_Timers.NextPoll-DB_HEARTBEAT;							-- Update timer
	if (DuckieBank_Timers.NextPoll<0) then														-- It's time
		if (not DuckNet_Idle(DUCKIEBANK_PREFIX)) then DuckieBank_BumpPoll(); return; end		-- Net is busy, so stall a bit
		local collateevent=nil; if (DuckieBank_CollateEvent=="Running") then collateevent=true; DuckieBank_CollateEvent="None"; end
		marker,stamp=DuckieBank_NextPoll(collateevent);
		if (marker and stamp) then
			if (DuckieBank_Poll.Section=="Bankers" and time()-stamp>DBT_INACTIVITY_BANKER) then		-- The banker is too old
				if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Removing banker "..DuckieBank_LastGuild[DuckieBank_Poll.Index].Banker..", as it is outdated"); end
				DuckieBank_RemoveBanker(DuckieBank_Poll.Index);
			else																								-- Poll network for this banker
				if (DuckNet_Poll(DUCKIEBANK_PREFIX,stamp,marker,DUCKIEBANK_VERSION_BANKNUM)==false) then
					if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Poll not initiated by DuckNet. Most likely other network-actions are active. Bumping..."); end
					DuckieBank_BumpPoll();
				end
			end
		end
	end

--[[		Snapshot-dispatcher		]]
	if (DuckieBank_Timers.StuffIt>=0) then			-- Activated
		DuckieBank_Timers.StuffIt=DuckieBank_Timers.StuffIt-DB_HEARTBEAT;
		if (DuckieBank_Timers.StuffIt<0) then
			DuckieBank_Timers.StuffIt=-1;
			DuckieBank_SaveItems();
		end
	end

--[[		Banker-dispatcher		]]
	if (DuckieBank_Timers.NextSend>0) then			-- Activated
		DuckieBank_Timers.NextSend=DuckieBank_Timers.NextSend-DB_HEARTBEAT;

		-- I'm a banker and my data is up next, so notify that it's gonna go in a few seconds
		if (DuckieBank_Banker and UnitName("player")==DuckieBank_BankerSend) then
			if (DuckieBank_Timers.NextSend<=DBT_INACTIVITY_NOTIFY and DuckieBank_Timers.UpdateNotified==false) then
				DuckieBank_Timers.UpdateNotified=true,	        	-- Upcoming update notified
				DuckieBank_Chat(string.format(DUCKIEBANK_NOTIFY_BROADCAST,DBT_INACTIVITY_NOTIFY));
			end
		end

		-- Time to send the stuff
		if (DuckieBank_Timers.NextSend<=0) then							-- Do it
			DuckieBank_Timers.NextSend=-1;								-- Timer done
			if (not DuckieBank_SendBank(DuckieBank_BankerSend)) then	-- Send the specified bank's contents
				if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Unable to send data. Waiting "..DBT_RESEND_DELAY.." seconds"); end
				DuckieBank_Timers.NextSend=DBT_RESEND_DELAY;			-- Bump send-timer
				DuckieBank_SendDelayed=DuckieBank_SendDelayed+DBT_RESEND_DELAY;
				if (DuckieBank_SendDelayed>DBT_RESEND_MAXIMUM) then
					DuckieBank_SendDelayed=0;
					DuckieBank_Ready=DuckieBank_Connect(true);			-- Perform silent unregister and re-register
					if (not DuckieBank_Ready) then return; end
					if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Re-registered with DuckNet"); end
				end
			else
				DuckieBank_Timers.NextSend=-1;							-- Timer done
				DuckieBank_SendDelayed=0;
			end
		end
	end

--[[		Collate bumper			]]
	if (DuckieBank_Timers.CollateBump>=0) then
		DuckieBank_Timers.CollateBump=DuckieBank_Timers.CollateBump-DB_HEARTBEAT;
		if (DuckieBank_Timers.CollateBump<0) then
			DuckieBank_Timers.CollateBump=-1;
			DuckieBank_CollateMyRequests();
		end
	end

--[[		Send-request bumper		]]
	if (DuckieBank_Timers.BumpSendRequests>=0) then
		DuckieBank_Timers.BumpSendRequests=DuckieBank_Timers.BumpSendRequests-DB_HEARTBEAT;
		if (DuckieBank_Timers.BumpSendRequests<0) then
			if (DuckieBank_SendRequests()~=DBV_RETRY) then DuckieBank_Timers.BumpSendRequests=-1; end
		end
	end

--[[		Send-settings bumper		]]
	if (DuckieBank_Timers.BumpSendSettings>=0) then
		DuckieBank_Timers.BumpSendSettings=DuckieBank_Timers.BumpSendSettings-DB_HEARTBEAT;
		if (DuckieBank_Timers.BumpSendSettings<0) then
			if (DuckieBank_SendSettings()~=DBV_RETRY) then DuckieBank_Timers.BumpSendSettings=-1; end
		end
	end
end


function DuckieBank_UpdateRoster()
	if (not IsInGuild()) then return; end
	GuildRoster();
end


function DuckieBank_BootBank()
	if (not DuckieBank_DataCache) then DuckieBank_DataCache=DuckieBank_Enumerate(); if (DuckieBank_DataCache) then DuckieBank_PopulateFilterDD(); end return; end
	DuckieBank_UpdateRoster();
	local state=DuckieBank_BankerState(); if (not state) then return; end;						-- Can't get the info yet
	DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then return; end
	DuckieBank_DBValidate232();

	if (DuckieBank_LastGuild["VerData"]) then
		if (DuckieBank_LastGuild["VerData"]<tonumber(DUCKIEBANK_VERSION_DATA)) then
			DuckNet_ClearTable(DuckieBank_LastGuild);
			DuckieBank_Chat(DUCKIEBANK_NOTIFY_WRONGVERSION);
		end
	end

	-- Do banker-mode
	if (state=="Banker") then
		DuckieBank_Banker=true;
		DuckieBank_Monitor=false;
		DuckieBank_Chat(DUCKIEBANK_NOTIFY_BANKERON);
		DuckieBank_SendBankerButton:Show();
		DuckieBank_ClearAllCosts:Show();
		DuckieBank_OpenBankerButton:Show();
		if (Auctioneer and Auctioneer.API and Auctioneer.API.GetVendorBuyPrice and Auctioneer.API.GetVendorSellPrice) then
			DuckieBank_FillCostVBuy:Show(); DuckieBank_FillCostVSell:Show();
		end
	else
		if (state=="Monitor") then DuckieBank_Monitor=true; end
		DuckieBank_Banker=false;
		DuckieBank_Chat(DUCKIEBANK_NOTIFY_READY);
		DuckieBank_SendBankerButton:Hide();
	end
	DuckieBank_PopulateDD();

	DuckieBank_Ready=DuckieBank_Connect();					-- Connect to the data-channel
	if (not DuckieBank_Ready) then return; end;
	DuckieBank_SnapshotTimer=DBT_INACTIVITY_DELAY;

	DuckieBank_CleanupRequests();
	DuckieBank_Timers.NextPoll=20;
	DBT_INTERVAL_POLL=math.random(240,300);
	DuckieBank_CollateEvent="Running";							-- Force update of requests first
	DuckieBank_Loader.index=1; DuckieBank_Loader.entry=1;		-- Start it

	return;
end


function DuckieBank_NextPoll(pollrequests)
	if (not DuckieBank_LastGuild) then return nil,nil; end
	local bankers=DuckieBank_CountBankers();
	local requests=DuckieBank_CountRequestBlocks();
	if (bankers<1 and requests<1) then return nil,nil; end

	DuckieBank_Timers.NextPoll=DBT_INTERVAL_POLL;
	DuckieBank_Poll.Index=DuckieBank_Poll.Index+1;

	if (pollrequests and requests>0) then									-- Force update of requests
		DuckieBank_Poll.Section="Requests";
		DuckieBank_Poll.Index=1;											-- One less to accomodate for next increase
	elseif (DuckieBank_Poll.Section=="Requests") then
		if (DuckieBank_Poll.Index>requests) then							-- Wrap
			if (bankers>0) then DuckieBank_Poll.Section="Bankers"; end
			DuckieBank_Poll.Index=1;
		end
	elseif (DuckieBank_Poll.Section=="Bankers") then
		if (DuckieBank_Poll.Index>bankers) then			-- Wrap
 			if (requests>0) then DuckieBank_Poll.Section="Requests"; end
			DuckieBank_Poll.Index=1;
		end
	else																	-- Wrap
		DuckieBank_Poll.Section="Requests"; DuckieBank_Poll.Index=1;
	end

	if (bankers>0 and DuckieBank_Poll.Section=="Bankers" and DuckieBank_Poll.Index==bankers) then		-- About to poll last banker
		DuckieBank_Timers.NextPoll=DBT_INTERVAL_POLLREST;				-- Please dont change this as it spams the server. May cause lag (and even DC in worst case)
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Resting for "..(DBT_INTERVAL_POLLREST / 60).." minutes after this poll"); end
	end

	-- Make request-type markers: DUCKIEBANK_REQUEST,banker,player
	if (DuckieBank_Poll.Section=="Requests") then marker,player,stamp=DuckieBank_GetRequestBlockInfo(DuckieBank_Poll.Index); marker=DUCKIEBANK_REQUEST..","..marker..","..player;
	elseif (DuckieBank_Poll.Section=="Bankers") then
		marker=DuckieBank_LastGuild[DuckieBank_Poll.Index].Banker;
		stamp=DuckieBank_LastGuild[DuckieBank_Poll.Index].Stamp;
		if (string.find(marker,",")) then DuckieBank_RemoveBanker(DuckieBank_Poll.Index); return nil,nil; end	-- Remains from mix with v2.20
	else return nil,nil; end
	if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Polling "..marker.." @ "..stamp); end

	return marker,stamp;
end


function DuckieBank_BumpPoll(now)
	if (not now) then
		if (DuckieBank_Timers.NextPoll>60) then return; end			-- At least 60 seconds remaining, so leave it
		now=DuckieBank_Timers.NextPoll;								-- Make offset
	else
		now=time();													-- Recalc to offset from now
	end
	DuckieBank_Timers.NextPoll=now+math.random(20,60);				-- Set new jittery poll-timer
end


function DuckieBank_CacheItem(item)
	if (type(item)=="number") then item="item:"..item;
	elseif (type(item)~="string") then return true; end
	local name=GetItemInfo(item);
	if (not GameTooltip:IsVisible()) then
		if (not name and DuckieBank_CacheRetries<10) then
			GameTooltip:SetOwner(DuckieBank_HiddenButton); GameTooltip:SetHyperlink(item); GameTooltip:Hide();
			DuckieBank_CacheRetries=DuckieBank_CacheRetries+1;
			return false;
		else
			DuckieBank_CacheRetries=0;
		end
	end
	return true;
end


function DuckieBank_Enumerate()
	_,_,_,_,_,DBT_ARMOR=GetItemInfo(24686);			if (not DBT_ARMOR) then DuckieBank_CacheItem(24686); return false; end
	_,_,_,_,_,DBT_CONSUMABLE=GetItemInfo(22829);	if (not DBT_CONSUMABLE) then DuckieBank_CacheItem(22829); return false; end
	_,_,_,_,_,DBT_CONTAINER=GetItemInfo(5765);		if (not DBT_CONTAINER) then DuckieBank_CacheItem(5765); return false; end
	_,_,_,_,_,DBT_REAGENT=GetItemInfo(22576);		if (not DBT_REAGENT) then DuckieBank_CacheItem(22576); return false; end
	_,_,_,_,_,DBT_RECIPE=GetItemInfo(28282);		if (not DBT_RECIPE) then DuckieBank_CacheItem(28282); return false; end
	_,_,_,_,_,DBT_PROJECTILE=GetItemInfo(8069);		if (not DBT_PROJECTILE) then DuckieBank_CacheItem(8069); return false; end
	_,_,_,_,_,DBT_QUEST=GetItemInfo(31812);			if (not DBT_QUEST) then DuckieBank_CacheItem(31812); return false; end
	_,_,_,_,_,DBT_QUIVER=GetItemInfo(11362);		if (not DBT_QUIVER) then DuckieBank_CacheItem(11362); return false; end
	_,_,_,_,_,DBT_TRADEGOODS=GetItemInfo(23112);	if (not DBT_TRADEGOODS) then DuckieBank_CacheItem(23112); return false; end
	_,_,_,_,_,DBT_WEAPON=GetItemInfo(25306);		if (not DBT_WEAPON) then DuckieBank_CacheItem(25306); return false; end

	if (DuckieBank_LS.Debug) then
		if (not DBT_ARMOR) then DuckieBank_Chat("Could not enumerate DBT_ARMOR"); end
		if (not DBT_CONSUMABLE) then DuckieBank_Chat("Could not enumerate DBT_CONSUMABLE"); end
		if (not DBT_CONTAINER) then DuckieBank_Chat("Could not enumerate DBT_CONTAINER"); end
		if (not DBT_REAGENT) then DuckieBank_Chat("Could not enumerate DBT_REAGENT"); end
		if (not DBT_RECIPE) then DuckieBank_Chat("Could not enumerate DBT_RECIPE"); end
		if (not DBT_PROJECTILE) then DuckieBank_Chat("Could not enumerate DBT_PROJECTILE"); end
		if (not DBT_QUEST) then DuckieBank_Chat("Could not enumerate DBT_QUEST"); end
		if (not DBT_QUIVER) then DuckieBank_Chat("Could not enumerate DBT_QUIVER"); end
		if (not DBT_TRADEGOODS) then DuckieBank_Chat("Could not enumerate DBT_TRADEGOODS"); end
		if (not DBT_WEAPON) then DuckieBank_Chat("Could not enumerate DBT_WEAPON"); end
	end

	return true;
end


-- An event has been received
function DuckieBank_OnEvent(event)
	-- Check basic stuff needed
	if (event=="VARIABLES_LOADED") then
		if (DuckieBank_LS.IconX and DuckieBank_LS.IconY) then DuckieBank_SetIconAbsolute(DuckieBank_LS.IconX,DuckieBank_LS.IconY);
		else DuckieBank_SetIconAngle(DuckieBank_LS.IconPosition); end
		DuckieBank_DataLoaded=true;
	end
	if (DuckieBank_Ready~=true) then return; end

	if (event=="BANKFRAME_OPENED") then
		DuckieBank_SnapshotTimer=0;
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Snapshot set to "..DuckieBank_SnapshotTimer.." seconds"); end
	elseif (event=="BANKFRAME_CLOSED") then
		DuckieBank_SnapshotTimer=DBT_INACTIVITY_DELAY;
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Snapshot set to "..DuckieBank_SnapshotTimer.." seconds"); end
	end

	-- Any required update from the Banker
	if (event=="BANKFRAME_OPENED" or event=="PLAYERBANKSLOTS_CHANGED" or event=="PLAYERBANKBAGSLOTS_CHANGED" or event=="BAG_UPDATE") then
		DuckieBank_Timers.StuffIt=DuckieBank_SnapshotTimer;
		return;
	end

	if (event=="PLAYER_GUILD_UPDATE") then
		if (not IsInGuild()) then
			DuckieBank_Ready=false;
			DuckieBank_LastGuild=nil;
		end
		return;
	end

	if (event=="GUILD_ROSTER_UPDATE" and DuckieBank_Ready) then DuckieBank_ValidateRoster(); return; end
end


function DuckieBank_ValidateRoster()
	local seq=1;
	local ranks=nil;
	local name,rank,_,_,_,_,pnote,onote=GetGuildRosterInfo(seq);

	-- Check ranks
	while(name and rank) do
		rank=string.lower(rank);
		if (rank==DB_BANKERRANK) then ranks=true; break; end
		seq=seq+1;
		name,rank,_,_,_,_,pnote,onote=GetGuildRosterInfo(seq);
	end

	if (ranks) then
		local i=1;
		while (DuckieBank_LastGuild[i] and not DuckieBank_LastGuild[i].Banker) do
			seq=1;
			local name,rank,_,_,_,_,pnote,onote=GetGuildRosterInfo(seq);
			while(name and rank) do
				rank=string.lower(rank);
				if (name==DuckieBank_LastGuild[i].Banker and rank~=DB_BANKERRANK) then DuckieBank_RemoveBanker(i,true); break; end
				seq=seq+1;
				name,rank,_,_,_,_,pnote,onote=GetGuildRosterInfo(seq);
			end
			i=i+1;
		end
	end

	if (not DuckieBank_LS.NotAgressive) then
		local i=1;
		while (DuckieBank_LastGuild[i]) do
			if (not DuckieBank_LastGuild[i].Banker) then return; end
			seq=1;
			local found=nil;
			local name,rank,_,_,_,_,pnote,onote=GetGuildRosterInfo(seq);
			while(name and rank) do
				if (name==DuckieBank_LastGuild[i].Banker) then found=true; end
				seq=seq+1;
				name,rank,_,_,_,_,pnote,onote=GetGuildRosterInfo(seq);
			end
			if (not found) then DuckieBank_RemoveBanker(i,true); end
			i=i+1;
		end
	end
end


-- Make update for show
function DuckieBank_Frame_OnShow()
	PlaySound("igMainMenuOpen");
	-- Flag it if it's not
	if (not DuckieBank_AFrame.hasShown) then DuckieBank_AFrame.hasShown=1; end
	DuckieBank_SetWindowState();
end


function DuckieBank_BankerFrame_OnShow()
	if (not DuckieBank_BankerFrame_Requests:IsVisible() and not DuckieBank_BankerFrame_Options:IsVisible()) then
		DuckieBank_ShowRequests();
	end
end


function DuckieBank_ShowRequests()
	DuckieBank_BankerFrame_Options:Hide();
	DuckieBank_BankerFrame_Requests:Show();
end


function DuckieBank_ShowOptions()
	DuckieBank_BankerFrame_Options:Show();
	DuckieBank_BankerFrame_Requests:Hide();
end


function DuckieBank_SetWindowState()
	if (DuckieBank_Banker) then
		DuckieBank_SendBankerButton:Show();
		DuckieBank_ClearAllCosts:Show();
		DuckieBank_OpenBankerButton:Show();
		if (Auctioneer and Auctioneer.API and Auctioneer.API.GetVendorBuyPrice and Auctioneer.API.GetVendorSellPrice) then
			DuckieBank_FillCostVBuy:Show(); DuckieBank_FillCostVSell:Show();
		end
	else
		DuckieBank_SendBankerButton:Hide();
		DuckieBank_ClearAllCosts:Hide();
		DuckieBank_OpenBankerButton:Hide();
		DuckieBank_FillCostVBuy:Hide(); DuckieBank_FillCostVSell:Hide();
	end
end


-- Clean-up and fixing
function DuckieBank_Frame_OnHide()
	if (DuckieBank_NewRequests) then DuckieBank_SendRequests(); end
end


-- There's slashing to be done
function DuckieBank_Slasher(msg,quiet)
	if (DuckieBank_Ready~=true) then return; end;

-- Validate input
	if (not msg) then msg=""; end
	if (strlen(msg)>0) then msg=strlower(msg); end

-- Check command
	DuckieBank_LastGuild=DuckieBank_TableSection();

    -- Open Main Frame
    if (msg=="open") then DuckieBank_OpenMainFrame(); return; end
    
    -- Reset frame positions
	if (msg=="resetpos") then DuckieBank_ResetPos(); return; end

	-- Kill entire register
	if (msg=="killall") then
		DuckNet_ClearTable(DuckieBank_LastGuild);
		if (not quiet) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_KILLALL); end
		return;
	end

	if (msg=="clean") then DuckieBank_UpdateRoster(); return; end

	if (msg=="agressive") then
		if (not DuckieBank_LS.NotAgressive) then
			DuckieBank_LS.NotAgressive=true;
			DuckieBank_Chat(DUCKIEBANK_NOTIFY_AGRESSIVE_ON);
		else
			DuckieBank_LS.NotAgressive=nil;
			DuckieBank_Chat(DUCKIEBANK_NOTIFY_AGRESSIVE_OFF);
		end
		return;
	end

	if (msg=="debug") then
		if (DuckieBank_LS.Debug~=true) then DuckieBank_LS.Debug=true; DuckieBank_Chat("DEBUG: ON");
		else DuckieBank_LS.Debug=false; DuckieBank_Chat("DEBUG: OFF"); end
		return;
	end

	if (msg=="debug server") then
		if (DuckieBank_LS.DebugServer~=true) then DuckieBank_LS.DebugServer=true; DuckieBank_Chat("DEBUG: Verbose server ON");
		else DuckieBank_LS.DebugServer=false; DuckieBank_Chat("DEBUG: Verbose server OFF"); end
		return;
	end

	-- Banker-specific switches
	if (DuckieBank_Banker) then
		-- Money display
		if (string.sub(msg,1,5)=="money") then
			if (msg=="money on") then
				DuckieBank_LS.ShowMoney=true;
				DuckieBank_SaveItems();
				if (not quiet) then DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_MONEY_ON); end
				return;
			elseif (msg=="money off") then
				DuckieBank_LS.ShowMoney=false;
				DuckieBank_SaveItems();
				if (not quiet) then DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_MONEY_OFF); end
				return;
			end
		end
		-- Bag status
		if (string.sub(msg,1,3)=="bag") then
			local bagnum=tonumber(string.sub(msg,4,4));
			if (bagnum and bagnum>=0 and bagnum<=4) then
				local state=string.sub(msg,6);
				if (state=="on" or state=="off") then
					if (state=="on") then state=true; else state=nil; end
					DuckieBank_LS["Bag"..bagnum]=state;
				end
			end
			if (quiet) then return; end

			if (not DuckieBank_LS.Bag0 and not DuckieBank_LS.Bag1 and not DuckieBank_LS.Bag2 and not DuckieBank_LS.Bag3 and not DuckieBank_LS.Bag4) then
				DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_BAGS_NONE);
			elseif (DuckieBank_LS.Bag0 and DuckieBank_LS.Bag1 and DuckieBank_LS.Bag2 and DuckieBank_LS.Bag3 and DuckieBank_LS.Bag4) then
				DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_BAGS_ALL);
			else
				DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_BAGS_NUMBERING);
				local text;
				if (DuckieBank_LS.Bag0) then text=DUCKIEBANK_NOTIFY_BAGS_INCLUDED; else text=DUCKIEBANK_NOTIFY_BAGS_EXCLUDED; end
				DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_BAGS_PREFIX0..text..DUCKIEBANK_NOTIFY_BAGS_POSTFIX);
				if (DuckieBank_LS.Bag1) then text=DUCKIEBANK_NOTIFY_BAGS_INCLUDED; else text=DUCKIEBANK_NOTIFY_BAGS_EXCLUDED; end
				DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_BAGS_PREFIX1..text..DUCKIEBANK_NOTIFY_BAGS_POSTFIX);
				if (DuckieBank_LS.Bag2) then text=DUCKIEBANK_NOTIFY_BAGS_INCLUDED; else text=DUCKIEBANK_NOTIFY_BAGS_EXCLUDED; end
				DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_BAGS_PREFIX2..text..DUCKIEBANK_NOTIFY_BAGS_POSTFIX);
				if (DuckieBank_LS.Bag3) then text=DUCKIEBANK_NOTIFY_BAGS_INCLUDED; else text=DUCKIEBANK_NOTIFY_BAGS_EXCLUDED; end
				DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_BAGS_PREFIX3..text..DUCKIEBANK_NOTIFY_BAGS_POSTFIX);
				if (DuckieBank_LS.Bag4) then text=DUCKIEBANK_NOTIFY_BAGS_INCLUDED; else text=DUCKIEBANK_NOTIFY_BAGS_EXCLUDED; end
				DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_BAGS_PREFIX4..text..DUCKIEBANK_NOTIFY_BAGS_POSTFIX);
			end
			return;
		end
		-- Send data
		if (msg=="send") then
			DuckieBank_BankerSend=UnitName("player");
			DuckieBank_Timers.UpdateNotified=true;
			DuckieBank_Timers.NextSend=3;			-- Send in 3 seconds
			return;
		end
		-- Price stacking option
		if (msg=="stack") then
			if (DuckieBank_LS.SingleCost) then
				DuckieBank_LS.SingleCost=nil;
				if (not quiet) then DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_STACK_MULTIPLY); end
			else
				DuckieBank_LS.SingleCost=true;
				if (not quiet) then DuckieBank_Chat("-> "..DUCKIEBANK_NOTIFY_STACK_SINGLE); end
			end
			return;
		end
	end

	-- Brief help triggered by any unknown (or empty) instruction
	DuckieBank_Chat(DUCKIEBANK_VERSIONTEXT);
	DuckieBank_Chat(SLASH_DUCKIEBANK2.." ("..SLASH_DUCKIEBANK1..")"..DUCKIEBANK_HELP_HEADER);
	DuckieBank_Chat("-> "..SLASH_DUCKIEBANK2.." open :  "..DUCKIEBANK_HELP_OPEN);
    DuckieBank_Chat("-> "..SLASH_DUCKIEBANK2.." resetpos :  "..DUCKIEBANK_HELP_RESETPOS);
	DuckieBank_Chat("-> "..SLASH_DUCKIEBANK2.." killall :  "..DUCKIEBANK_HELP_KILLALL);
	if (DuckieBank_Banker) then
		DuckieBank_Chat("-> "..SLASH_DUCKIEBANK2.." money ON|OFF :  "..DUCKIEBANK_HELP_MONEYONOFF);
		DuckieBank_Chat("-> "..SLASH_DUCKIEBANK2.." stack :  "..DUCKIEBANK_HELP_STACK);
		DuckieBank_Chat("-> "..SLASH_DUCKIEBANK2.." send :  "..DUCKIEBANK_HELP_SEND);
		DuckieBank_Chat("-> "..SLASH_DUCKIEBANK2.." bag :  "..DUCKIEBANK_HELP_BAG);
		DuckieBank_Chat("-> "..SLASH_DUCKIEBANK2.." bag<0-4> ON|OFF :  "..DUCKIEBANK_HELP_BAGnONOFF.."\"/db bag4 off\")");
	end
end


function DuckieBank_OptionsSave()
	DuckieBank_BankerFrame_Options_KillAllConfirm:Hide();
	if (this==DuckieBank_BankerFrame_Options_KillAllConfirm) then DuckNet_ClearTable(DuckieBank_LastGuild); return; end

	DuckieBank_LS.Bag0=DuckieBank_BankerFrame_Options_Bag0:GetChecked();
	DuckieBank_LS.Bag1=DuckieBank_BankerFrame_Options_Bag1:GetChecked();
	DuckieBank_LS.Bag2=DuckieBank_BankerFrame_Options_Bag2:GetChecked();
	DuckieBank_LS.Bag3=DuckieBank_BankerFrame_Options_Bag3:GetChecked();
	DuckieBank_LS.Bag4=DuckieBank_BankerFrame_Options_Bag4:GetChecked();
	DuckieBank_LS.ShowMoney=DuckieBank_BankerFrame_Options_Money:GetChecked();
	if (DuckieBank_BankerFrame_Options_StackMultiply:GetChecked()) then DuckieBank_LS.SingleCost=false; else DuckieBank_LS.SingleCost=true; end
	DuckieBank_Timers.StuffIt=DuckieBank_SnapshotTimer;
	DuckieBank_Ready=false;
end


function DuckieBank_ShowOptionsFrame()
	DuckieBank_BankerFrame_Options_KillAllConfirm:Hide();
	DuckieBank_BankerFrame_Options_Bag0:SetChecked(DuckieBank_LS.Bag0);
	DuckieBank_BankerFrame_Options_Bag1:SetChecked(DuckieBank_LS.Bag1);
	DuckieBank_BankerFrame_Options_Bag2:SetChecked(DuckieBank_LS.Bag2);
	DuckieBank_BankerFrame_Options_Bag3:SetChecked(DuckieBank_LS.Bag3);
	DuckieBank_BankerFrame_Options_Bag4:SetChecked(DuckieBank_LS.Bag4);

	DuckieBank_BankerFrame_Options_Money:SetChecked(DuckieBank_LS.ShowMoney);
	if (DuckieBank_LS.SingleCost) then DuckieBank_BankerFrame_Options_StackMultiply:SetChecked(false); else DuckieBank_BankerFrame_Options_StackMultiply:SetChecked(true); end
end


function DuckieBank_DBValidate232()
	local seq=1;
	while(DuckieBank_LastGuild[seq]) do
		if (not DuckieBank_LastGuild[seq].Banker) then DuckieBank_RemoveBanker(seq);
		else
			if (string.find(DuckieBank_LastGuild[seq].Banker,",")) then DuckieBank_RemoveBanker(seq);
			else
				if (not DuckieBank_LastGuild[seq].Stamp) then DuckieBank_LastGuild[seq].Stamp=time()-(DBT_INACTIVITY_BANKER-172800); end	-- 2 days
				if (not DuckieBank_LastGuild[DuckieBank_LastGuild[seq].Banker]) then DuckieBank_LastGuild[DuckieBank_LastGuild[seq].Banker]={}; end
				seq=seq+1;
			end
		end
	end
end


-- Banker notes handling
function DuckieBank_BankerTextChanged()
	if (DuckieBank_Banker~=true) then return; end;
	if (not DuckieBank_BankerSelectDropDown.selectedID) then return; end
	if (not DuckieBank_LastGuild[DuckieBank_BankerSelectDropDown.selectedID]) then return; end;
	if (not DuckieBank_LastGuild[DuckieBank_BankerSelectDropDown.selectedID].Banker) then return; end;
	if (DuckieBank_LastGuild[DuckieBank_BankerSelectDropDown.selectedID].Banker~=UnitName("player")) then return; end;

	local index=DuckieBank_BankerIndex(UnitName("player")); if (not index) then return; end;
	index.Notes=DuckieBank_BankerNotesEdit:GetText();
end

function DuckieBank_BankerTextEscape()
	if (DuckieBank_Banker~=true) then return; end;
	DuckieBank_BankerNotesEdit:SetText(DuckieBank_OldBankerNotes);
	DuckieBank_BankerNotesEdit:ClearFocus();
	if (DuckieBank_Banker~=true) then return; end;
	local index=DuckieBank_BankerIndex(UnitName("player"));
	if (not index) then return; end;
	index.Notes=DuckieBank_OldBankerNotes;
end


-- The resetting-bissniss
function DuckieBank_ResetPos()
	DuckieBank_AFrame:ClearAllPoints();
	DuckieBank_AFrame:SetPoint("TOPLEFT","UIParent","TOPLEFT",50,-104);
end


-- Return pointer to this banker's price-list
function DuckieBank_BankerCost()
	if (not DuckieBank_Banker) then return nil; end;
	local bankername=UnitName("player");
	if (not DuckieBank_LS[bankername]) then DuckieBank_LS[bankername]={}; end;
	return DuckieBank_LS[bankername];
end

-- Return pointer to a banker's items
function DuckieBank_BankerItems(bankername)
	if (not bankername) then
		if (not DuckieBank_Banker) then return nil; end;
		bankername=UnitName("player");
	end;
	if (DuckieBank_LastGuild[bankername]) then return DuckieBank_LastGuild[bankername]; end;

	return nil;
end

-- Return pointer to a banker's contents-index
function DuckieBank_BankerIndex(bankername,create)
	if (not DuckieBank_LastGuild) then return nil; end;
	if (not bankername) then
		if (not DuckieBank_Banker) then return nil; end;
		bankername=UnitName("player");
	end;
	local i=1;
	while (DuckieBank_LastGuild[i]) do
		if (not DuckieBank_LastGuild[i].Banker) then break; end;
		if (DuckieBank_LastGuild[i].Banker==bankername) then return DuckieBank_LastGuild[i]; end;
		i=i+1;
	end

	if (create==true) then
		DuckieBank_LastGuild[i]={};
		DuckieBank_LastGuild[i].Banker=bankername;
		DuckieBank_LastGuild[bankername]={};
		return DuckieBank_LastGuild[i];
	end;

	return nil;
end


function DuckieBank_CheckLocalBankerData()
	local name=UnitName("player");
	if (not DuckieBank_LS[name]) then DuckieBank_LS[name]={}; end;
	if (not DuckieBank_LS[name].BankItemCount) then DuckieBank_LS[name].BankItemCount=0; end;
	if (not DuckieBank_LS[name].Cost) then DuckieBank_LS[name].Cost={}; end;
end


function DuckieBank_RegisterItem(seq,icon,count,link,splitsecond)
	DuckieBank_LS[DuckieBank_BankerSend][seq]={ ["icon"]=icon, ["count"]=count, ["link"]=link };
	DuckieBank_LS[DuckieBank_BankerSend][seq].cost=DuckieBank_GetItemLocalCost(DuckieBank_LS[DuckieBank_BankerSend][seq]);
	if (not splitsecond) then
		DuckieBank_LS.Age[link]=nil;
		return;
	end
	link=DuckieBank_GetUniqueItem(link);
	if (not DuckieBank_LS.Age) then DuckieBank_LS.Age={}; end
	if (not DuckieBank_LS.Age[link]) then DuckieBank_LS.Age[link]={}; end
	DuckieBank_LS.Age[link].found=true;
	if (not DuckieBank_LS.Age[link].Time) then DuckieBank_LS.Age[link].Time=splitsecond; end	-- Don't have it, so set date
end


function DuckieBank_SetItemsUnfound()
	if (not DuckieBank_LS.Age) then return; end

	local seq=0;
	local link=nil;
	for num=1,28 do link=GetContainerItemLink(BANK_CONTAINER,num); if (link) then seq=seq+1; break; end end
	if (seq==0) then
		for num=5,11 do
			local bagslots=GetContainerNumSlots(num);
			for item=1,bagslots do link=GetContainerItemLink(num,item); if (link) then seq=seq+1; break; end end
		end
	end

	if (seq>0) then		-- At bank
		for link,lTable in pairs(DuckieBank_LS.Age) do lTable.found=nil; end		-- Unfound all
	else				-- Not at bank
		local ThisBanker=DuckieBank_LS[DuckieBank_BankerSend]; if (not ThisBanker) then return; end
		local offset=DuckieBank_LS[DuckieBank_BankerSend].BankItemCount;
		for link,lTable in pairs(DuckieBank_LS.Age) do
			-- Set bag-contents unfound this scan
			seq=1; 
			while (ThisBanker[seq+offset]) do												-- Traverse previous bag-contents
				local here=ThisBanker[seq+offset];
				link=DuckieBank_GetUniqueItem(here.link);
				if (DuckieBank_LS.Age[link]) then DuckieBank_LS.Age[link].found=nil; end	-- It's already stamped, so set unfound
				seq=seq+1;
			end
			-- Set bank-contents found this scan since bank is not open. We would assume it's not gone while we've not visited it
			-- Doing this after bags will also ensure that overlapping bank- and carry-items wont conflict
			seq=1;
			while (seq<=offset and ThisBanker[seq]) do										-- Traverse last known bank-contents
				local here=ThisBanker[seq];
				link=DuckieBank_GetUniqueItem(here.link);
				if (DuckieBank_LS.Age[link]) then DuckieBank_LS.Age[link].found=true; end
				seq=seq+1;
			end
		end
	end
end


function DuckieBank_RemoveDeadItems()
	if (not DuckieBank_LS.Age) then return; end
	for link,lTable in pairs(DuckieBank_LS.Age) do if (not lTable.found) then DuckieBank_LS.Age[link]=nil; end end
end


-- Save bank in DB
function DuckieBank_SaveItems()
	if (not DuckieBank_Banker) then return; end;			-- None existing
	if (DuckieBank_Banker==false) then return; end;			-- Don't save if it's not the banker
	now=time();

	DuckieBank_Timers.UpdateNotified=false;
	DuckieBank_BankerSend=UnitName("player");
	DuckieBank_Timers.NextSend=DBT_INACTIVITY_SEND;			-- Set send timer

	-- Make stuff to work with
	local index=DuckieBank_BankerIndex(nil,true);		-- Create it if it's not there
	local ThisBanker=DuckieBank_BankerItems(nil,true);	-- Create it if it's not there
	local link,icon,quantity,bagslots;

	DuckieBank_CheckLocalBankerData();					-- Check (and make if need be) the local stuff for a banker

	-- Save some stuff about environment
	index.Stamp=time();						-- Make stamp
	index.VerData=tonumber(DUCKIEBANK_VERSION_DATA);
	index.VerBanker=tonumber(DUCKIEBANK_VERSION_BANKNUM);
	
	if (not DuckieBank_LS[DuckieBank_BankerSend]) then DuckieBank_LS[DuckieBank_BankerSend]={}; end
	if (DuckieBank_LS.ShowMoney) then index.Money=math.floor(GetMoney()/10000); else index.Money=nil; end

	DuckieBank_SetItemsUnfound();

	-- Do all bank slots
	local seq=0;
	for num=1,28 do
		link=GetContainerItemLink(BANK_CONTAINER,num);
		icon,quantity=GetContainerItemInfo(BANK_CONTAINER,num);
		if (link) then
			seq=seq+1;			-- Advance with 1-offset
			DuckieBank_RegisterItem(seq,icon,quantity,link,now);
		end
	end

	-- Each banker bag
	for num=5,11 do
		bagslots=GetContainerNumSlots(num);
		-- Each slot in said bag
		link=nil;
		for item=1,bagslots do
			link=GetContainerItemLink(num,item);
			icon,quantity=GetContainerItemInfo(num,item);
			if (link) then
				seq=seq+1;			-- Advance with 1-offset
				DuckieBank_RegisterItem(seq,icon,quantity,link,now);
			end
		end
	end
	if (seq>0) then DuckieBank_LS[DuckieBank_BankerSend].BankItemCount=seq; end

	-- Update in case bank is not open
	seq=DuckieBank_LS[DuckieBank_BankerSend].BankItemCount;
	-- Each player bag
	for num=0,4 do
		if (DuckieBank_LS["Bag"..num]) then
			bagslots=GetContainerNumSlots(num);
			-- Each slot in said bag
			link=nil;
			for item=1,bagslots do
				link=GetContainerItemLink(num,item);
				icon,quantity=GetContainerItemInfo(num,item);
				if (link) then
					seq=seq+1;			-- Advance with 1-offset
					DuckieBank_RegisterItem(seq,icon,quantity,link,now);
				end
			end
		end
	end

	-- Clear any following data
	while(DuckieBank_LS[DuckieBank_BankerSend][seq+1]) do seq=seq+1; DuckieBank_LS[DuckieBank_BankerSend][seq]=nil; end

	DuckieBank_RemoveDeadItems();					-- Remove items not in inventory

	-- Copy and sort
	DuckieBank_LastGuild[DuckieBank_BankerSend]=nil; DuckieBank_LastGuild[DuckieBank_BankerSend]=DuckNet_CopyTable(DuckieBank_LS[DuckieBank_BankerSend]);
	ThisBanker=DuckieBank_BankerItems(nil,true);	-- Create it if it's not there

	-- Squeeze
	seq=1;
	while(ThisBanker[seq]) do
		local pos=seq+1;
		local newcount=ThisBanker[seq].count;
		while(ThisBanker[pos]) do
			if (DuckieBank_GetUniqueItem(ThisBanker[seq].link)==DuckieBank_GetUniqueItem(ThisBanker[pos].link)) then
				newcount=newcount+ThisBanker[pos].count;
				local move=pos+1;
				while(ThisBanker[move]) do
					ThisBanker[move-1]=DuckNet_CopyTable(ThisBanker[move]);
					move=move+1;
				end
				ThisBanker[move-1]=nil;
			else pos=pos+1; end
		end
		ThisBanker[seq].count=newcount;
		seq=seq+1;
	end

--local itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemTexture = GetItemInfo(itemID or "itemString" or "itemName" or "itemLink") 
	-- Sort by level
	seq=1;
	while(ThisBanker[seq+1]) do
		local pos=seq;
		local _,_,_,_,level1=GetItemInfo(ThisBanker[seq].link);
		local _,_,_,_,level2=GetItemInfo(ThisBanker[seq+1].link);
		if (not level2) then level2=level1; elseif (not level1) then level1=level2; end
		if (level1 and level2) then								-- Do not attempt if an item is uncached
			while (pos>0 and level1>level2) do
				DuckieBank_Swap(ThisBanker,pos,pos+1);
				pos=pos-1;
				if (pos>0) then
					_,_,_,_,level1=GetItemInfo(ThisBanker[pos].link);
					_,_,_,_,level2=GetItemInfo(ThisBanker[pos+1].link);
					if (not level2) then level2=level1; elseif (not level1) then level1=level2; end
				end
			end
		end
		seq=seq+1;
	end

	-- Sort by subtype (mail, cloth, plate, etc - ench, tailoring, etc)
	seq=1;
	while(ThisBanker[seq+1]) do
		local pos=seq;
		local _,_,_,_,_,_,sub1=GetItemInfo(ThisBanker[seq].link);
		local _,_,_,_,_,_,sub2=GetItemInfo(ThisBanker[seq+1].link);
		if (not sub2) then sub2=sub1; elseif (not sub1) then sub1=sub2; end
		if (sub1 and sub2) then								-- Do not attempt if an item is uncached
			while (pos>0 and sub1>sub2) do
				DuckieBank_Swap(ThisBanker,pos,pos+1);
				pos=pos-1;
				if (pos>0) then
					_,_,_,_,_,_,sub1=GetItemInfo(ThisBanker[pos].link);
					_,_,_,_,_,_,sub2=GetItemInfo(ThisBanker[pos+1].link);
					if (not sub2) then sub2=sub1; elseif (not sub1) then sub1=sub2; end
				end
			end
		end
		seq=seq+1;
	end

	-- Sort by type
	seq=1;
	while(ThisBanker[seq+1]) do
		local pos=seq;
		local _,_,_,_,_,itemType1=GetItemInfo(ThisBanker[seq].link);
		local _,_,_,_,_,itemType2=GetItemInfo(ThisBanker[seq+1].link);
		if (not itemType2) then itemType2=itemType1; elseif (not itemType1) then itemType1=itemType2; end
		if (itemType1 and itemType2) then								-- Do not attempt if an item is uncached
			while (pos>0 and itemType1>itemType2) do
				DuckieBank_Swap(ThisBanker,pos,pos+1);
				pos=pos-1;
				if (pos>0) then
					_,_,_,_,_,itemType1=GetItemInfo(ThisBanker[pos].link);
					_,_,_,_,_,itemType2=GetItemInfo(ThisBanker[pos+1].link);
					if (not itemType2) then itemType2=itemType1; elseif (not itemType1) then itemType1=itemType2; end
				end
			end
		end
		seq=seq+1;
	end

	-- Update is needed
	if (DuckieBank_AFrame:IsVisible()) then
		DuckieBank_StuffBankItems();
	end
end


function DuckieBank_Swap(bank,pos1,pos2)
	local temp=DuckNet_CopyTable(bank[pos1]);
	bank[pos1]=DuckNet_CopyTable(bank[pos2]);
	bank[pos2]=DuckNet_CopyTable(temp);
end


function DuckieBank_MouseWheel(slider)
	if (not slider) then slider=this; end
	local min,max=slider:GetMinMaxValues(); if (not min or not max) then return; end
	local step=slider:GetValueStep(); if (not step) then return; end
	local value=slider:GetValue(); if (not value) then return; end
	if (arg1==1 and (value-step)>=min) then slider:SetValue(value-step);
	elseif (arg1==-1 and (value+step)<=max) then slider:SetValue(value+step); end
end


function DuckieBank_GetItemOverlay(link)
	_,_,_,_,_,itemType,itemSubType=GetItemInfo(link);
	if (DuckieBank_FilterSelectDropDown.selectedID>1 and itemType) then
		if (itemType~=DuckieBank_FilterName(DuckieBank_FilterSelectDropDown.selectedID)) then return "Interface\\Buttons\\UI-QuickslotRed" end
	end
	local marking="Interface\\AddOns\\DuckieBank\\Images\\Frame_";
	if (itemType==DBT_ARMOR) then marking=marking..ITEMCOLORRED;
	elseif (itemType==DBT_CONSUMABLE) then marking=marking..ITEMCOLORCYAN;
	elseif (itemType==DBT_CONTAINER) then marking=marking..ITEMCOLORGREEN;
	elseif (itemType==DBT_REAGENT) then marking=marking..ITEMCOLORPURPLE;
	elseif (itemType==DBT_RECIPE) then marking=marking..ITEMCOLORPINK;
	elseif (itemType==DBT_PROJECTILE) then marking=marking..ITEMCOLORBLUE;
	elseif (itemType==DBT_QUEST) then marking=marking..ITEMCOLORYELLOW;
	elseif (itemType==DBT_QUIVER) then marking=marking..ITEMCOLORGREEN;
	elseif (itemType==DBT_TRADEGOODS) then marking=marking..ITEMCOLORORANGE;
	elseif (itemType==DBT_WEAPON) then marking=marking..ITEMCOLORBLUE;
	else marking=nil; end
	return marking;
end


	-- There seem to be some problems with the setting of
	-- the players guild-name (it's slow), so this "LastGuild"
	-- test will provide a better UI, as it will not stuff things in
	-- there if it's not loaded yet.
-- Build it
function DuckieBank_StuffBankItems()
	DuckieBank_LastGuild=DuckieBank_TableSection();
	if (not DuckieBank_LastGuild) then
		if (DuckieBank_Ready) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); end
		return;
	end

	local dropframe=DuckieBank_BankerSelectDropDown;
	if (not dropframe) then return;
	elseif (not dropframe.selectedID) then
		UIDropDownMenu_SetSelectedID(dropframe,1);
		if (not dropframe.selectedID) then return; end;
	end
	if (not DuckieBank_LastGuild[dropframe.selectedID]) then return; end;
	if (not DuckieBank_LastGuild[dropframe.selectedID].Banker) then return; end;

	local texture,button;

	-- Find selected banker's stuff
	local selecteddude=DuckieBank_LastGuild[dropframe.selectedID].Banker;
	UIDropDownMenu_SetText(selecteddude,dropframe)

	local index=DuckieBank_BankerIndex(selecteddude); if (not index) then return; end;			-- Get this
	local ThisBanker=DuckieBank_BankerItems(selecteddude); if (not ThisBanker) then return; end;	-- And this

	if (index.Notes) then
		DuckieBank_OldBankerNotes=index.Notes;
		DuckieBank_BankerNotesEdit:SetText(index.Notes);
	else
		DuckieBank_OldBankerNotes="";
		DuckieBank_BankerNotesEdit:SetText("");
	end
	
	if (index.Money) then
		DuckieBank_ABankerMoney:Show();
		DuckieBank_ABankerMoney:SetText(DUCKIEBANK_TEXT_MONEY..": "..index.Money..DUCKIEBANK_ABR_GOLD);
	else
		DuckieBank_ABankerMoney:Hide();
	end

	local slider=DuckieBank_ItemSlider;		-- The slider
	local slidemax=0;
	local amount=table.maxn(ThisBanker);
	if (amount>40) then
		slidemax=amount-40;							-- 41-40=1
		slidemax=math.ceil(slidemax/8);				-- 1/8=1
		slidemax=slidemax*8;						-- 1x8=8
	end
	slider:SetMinMaxValues(0,slidemax);

	-- Update time and date
	local timestring="";
	if (not index.Stamp or index.Stamp<1) then timestring="";
	else timestring=date("%a, %d. %b",index.Stamp); end
	DuckieBank_AInfoText:SetText(DUCKIEBANK_VERSIONTEXT);
	DuckieBank_ABankerInfo:SetText(timestring);

	-- The new bank
	local seq=1; 
	local offset=slider:GetValue();

	while (seq<41 and ThisBanker[seq+offset]) do
		local here=ThisBanker[seq+offset];
		getglobal("DuckieBank_AItem"..seq.."Group"):SetTexture(DuckieBank_GetItemOverlay(here.link));
		button=getglobal("DuckieBank_AItem"..seq);
		SetItemButtonTexture(button,here.icon);
		SetItemButtonCount(button,here.count);
		if (DuckieBank_Banker and selecteddude==UnitName("player")) then
			getglobal("DuckieBank_AItem"..seq.."ItemCost"):SetText(DuckieBank_GetItemLocalCost(here));
		elseif (here.cost) then getglobal("DuckieBank_AItem"..seq.."ItemCost"):SetText(here.cost);
		else getglobal("DuckieBank_AItem"..seq.."ItemCost"):SetText(""); end;
		seq=seq+1;
	end

	-- fill empty
	while (seq<41) do
		button=getglobal("DuckieBank_AItem"..seq);
		SetItemButtonTexture(button,"");
		SetItemButtonCount(button,nil);
		getglobal("DuckieBank_AItem"..seq.."Group"):SetTexture("");
		getglobal("DuckieBank_AItem"..seq.."ItemCost"):SetText("");
		seq=seq+1;
	end
end


function DuckieBank_GetItemLocalCost(itemT,onlylink)
	local Cost=DuckieBank_BankerCost();					-- Get cost-list
	local item={};

	if (itemT) then item=itemT;
	elseif (onlylink) then item.link=onlylink; item.count=1;
	else return ""; end

	local link=DuckieBank_GetUniqueItem(item.link);
	if (not Cost[link]) then return ""; end;

	if (Cost[link].Type=="Money") then return DuckieBank_FormatItemCost(Cost[link].Cost,item.count);
	elseif (Cost[link].Type=="Custom" and Cost[link].Cost) then return tostring(Cost[link].Cost); end;
	return "";
end


-- Only to be called from a button
function DuckieBank_Button_SpewPrices()
	if (not DuckieBank_Banker) then return; end;
	local index=DuckieBank_BankerIndex(UnitName("player")); if (not index) then return; end;
	index.Stamp=time();						-- Make new stamp
	local Cost=DuckieBank_BankerCost();
	if (not Cost) then return; end;
	if (this~=DuckieBank_FillCostVBuy and this~=DuckieBank_FillCostVSell) then return; end

	-- Update price-list
	local ThisBanker=DuckieBank_BankerItems(UnitName("player"));									-- Get me
	local seq=1; while (ThisBanker[seq]) do
		local itemID=DuckieBank_GetUniqueItem(ThisBanker[seq].link);
		if (not Cost[itemID]) then Cost[itemID]={}; end;
		if (Cost[itemID].Cost==nil or Cost[itemID].Cost=="") then									-- Only empty slots
			Cost[itemID].Cost=DuckieBank_GetItemVendorCost(ThisBanker[seq],this);					-- Spew raw cost-data
			Cost[itemID].Type="Money";
		end
		seq=seq+1;
	end

	DuckieBank_Timers.UpdateNotified=false;
	DuckieBank_BankerSend=UnitName("player");
	DuckieBank_Timers.NextSend=DBT_INACTIVITY_SEND;			-- Set send timer
	DuckieBank_StuffBankItems();
end


function DuckieBank_Button_ClearAllCosts()
	if (not DuckieBank_Banker) then return; end;
	local index=DuckieBank_BankerIndex(UnitName("player")); if (not index) then return; end;
	index.Stamp=time();						-- Make new stamp
	local Cost=DuckieBank_BankerCost();
	if (not Cost) then return; end;
	if (this~=DuckieBank_ClearAllCosts) then return; end

	-- Update price-list
	local ThisBanker=DuckieBank_BankerItems(UnitName("player"));									-- Get me
	local seq=1; while (ThisBanker[seq]) do
		local itemID=DuckieBank_GetUniqueItem(ThisBanker[seq].link);
		if (Cost[itemID]) then Cost[itemID]=nil; end
		seq=seq+1;
	end

	DuckieBank_Timers.UpdateNotified=false;
	DuckieBank_BankerSend=UnitName("player");
	DuckieBank_Timers.NextSend=DBT_INACTIVITY_SEND;			-- Set send timer
	DuckieBank_StuffBankItems();
end


function DuckieBank_GetItemVendorCost(item,button)
	-- If using auctioneer prices
	if (Auctioneer and Auctioneer.API) then
		local itemID=DuckieBank_GetID(item.link);
		if (itemID) then
			if (button==DuckieBank_FillCostVBuy) then
				if (Auctioneer and Auctioneer.API and Auctioneer.API.GetVendorBuyPrice) then return Auctioneer.API.GetVendorBuyPrice(itemID); end;
			end
			if (button==DuckieBank_FillCostVSell) then
				if (Auctioneer and Auctioneer.API and Auctioneer.API.GetVendorSellPrice) then  return Auctioneer.API.GetVendorSellPrice(itemID); end;
			end
			return nil;
		end
	end
	return nil;
end


function DuckieBank_FormatItemCost(cost,mpx)
	if (not cost or cost<1) then return ""; end;
	if (not mpx) then mpx=1; end;
	if (DuckieBank_LS.SingleCost) then mpx=1; end

	-- When price is money
	cost=cost*mpx;
	local g=floor(cost/10000); cost=cost-(g*10000);
	local s=floor(cost/100); cost=cost-(s*100);
	local c=cost;

	-- If both gold and copper, round coppers
	if (g>0 and c>0) then if (c>=50) then s=s+1; if (s==100) then s=0; g=g+1; end; end; c=0; end;

	local text="";
	if (g>0) then text=text..g..DUCKIEBANK_ABR_GOLD; end;
	if (s>0) then text=text..s..DUCKIEBANK_ABR_SILVER; end;
	if (c>0) then text=text..c..DUCKIEBANK_ABR_COPPER; end;
	return text;
end


function DuckieBank_GetUniqueItem(link)
	if (type(link)~="string") then return nil; end;
	itemID,i1,i2,i3,i4,i5,i6=link:match("|Hitem:(%p?%d+):(%p?%d+):(%p?%d+):(%p?%d+):(%p?%d+):(%p?%d+):(%p?%d+):(%p?%d+)|h%[(.-)%]|h");
	return "item:"..itemID..":"..i1..":"..i2..":"..i3..":"..i4..":"..i5..":"..i6;
end

function DuckieBank_GetID(link)
	if (type(link)~="string") then return nil; end;
	local itemid=link:match("|Hitem:(%p?%d+):(%p?%d+):(%p?%d+):(%p?%d+):(%p?%d+):(%p?%d+):(%p?%d+):(%p?%d+)|h%[(.-)%]|h");
	return tonumber(itemid);
end


-- Find the banker button thingie
function DuckieBank_Button_Selector()
	if (not DuckieBank_LastGuild) then return nil; end;
	local dropframe=DuckieBank_BankerSelectDropDown;
	if (not dropframe) then return nil;						-- It's not there
	elseif (not dropframe.selectedID) then					-- None selected
		UIDropDownMenu_SetSelectedID(dropframe,1);			-- Select first entry
	end

	if (not DuckieBank_LastGuild[dropframe.selectedID]) then return nil; end;
	if (not DuckieBank_LastGuild[dropframe.selectedID].Banker) then return nil; end;
	local ThisBanker=DuckieBank_BankerItems(DuckieBank_LastGuild[dropframe.selectedID].Banker);
	if (not ThisBanker) then return nil; end

	local slider=DuckieBank_ItemSlider;		-- The slider
	local value=ThisBanker[this:GetID()+slider:GetValue()];
    return value;
end

-- Banker-button mouseover
function DuckieBank_Button_OnEnter()
	if (not DuckieBank_LastGuild) then return; end;
	if (this==DuckieBank_FillCostVBuy or this==DuckieBank_FillCostVSell or this==DuckieBank_ClearAllCosts) then
		local dropframe=DuckieBank_BankerSelectDropDown;
		if (not DuckieBank_LastGuild[dropframe.selectedID]) then return; end;
		if (not DuckieBank_LastGuild[dropframe.selectedID].Banker) then return; end;
		if (DuckieBank_LastGuild[dropframe.selectedID].Banker==UnitName("player")) then
			local text=" ";
			if (this==DuckieBank_FillCostVBuy) then text=DUCKIEBANK_HOVER_COSTVBUY;
			elseif (this==DuckieBank_FillCostVSell) then text=DUCKIEBANK_HOVER_COSTVSELL;
			elseif (this==DuckieBank_ClearAllCosts) then text=DUCKIEBANK_HOVER_COSTCLEAR;
			end
			GameTooltip:SetOwner(this,"ANCHOR_RIGHT");
			GameTooltip:SetText(text);
			GameTooltip:AddLine("("..DUCKIEBANK_HOVER_COSTSETCUSTOM..")");
			GameTooltip:Show();
		end
		return;
	elseif (this==DuckieBank_RequestItem) then
		GameTooltip:SetOwner(this,"ANCHOR_RIGHT");
		GameTooltip:SetText(DUCKIEBANK_HOVER_REQUESTEDITEM);
		GameTooltip:AddLine(DUCKIEBANK_HOVER_REQUESTEDITEMLC1);
		GameTooltip:AddLine(DUCKIEBANK_HOVER_REQUESTEDITEMLC2);
		GameTooltip:AddLine(DUCKIEBANK_HOVER_REQUESTEDITEMSLC);
		GameTooltip:Show();
		return;
	elseif (this==DuckieBank_BankerFrame_RequestsItem1 or this==DuckieBank_BankerFrame_RequestsItem2 or
			this==DuckieBank_BankerFrame_RequestsItem3 or this==DuckieBank_BankerFrame_RequestsItem4) then
		local item=nil;
		if (this==DuckieBank_BankerFrame_RequestsItem1) then item=DuckieBank_BankerFrame_RequestsItem1Remove.Item;
		elseif (this==DuckieBank_BankerFrame_RequestsItem2) then item=DuckieBank_BankerFrame_RequestsItem2Remove.Item;
		elseif (this==DuckieBank_BankerFrame_RequestsItem3) then item=DuckieBank_BankerFrame_RequestsItem3Remove.Item;
		elseif (this==DuckieBank_BankerFrame_RequestsItem4) then item=DuckieBank_BankerFrame_RequestsItem4Remove.Item;
		end
		if (item) then
			local ThisItem=DuckieBank_RL[item].itemID;
			GameTooltip:SetOwner(this,"ANCHOR_RIGHT");
			if (not string.find(ThisItem,":")) then _,ThisItem=GetItemInfo(ThisItem);  end
			GameTooltip:SetHyperlink(ThisItem);
		end
		return;
	end

	if (not ChatFrameEditBox:IsVisible()) then SetCursor("BUY_CURSOR"); end
	local value=DuckieBank_Button_Selector();
	if (not value) then return; end;

	if (value.link and strlen(value.link)>0) then
		GameTooltip:SetOwner(this,"ANCHOR_RIGHT");
		GameTooltip:SetHyperlink(value.link);
		local age=DuckieBank_GetItemAge(value.link);
		if (age and DuckieBank_Banker) then
			GameTooltip:AddLine(DUCKIEBANK_TEXT_ITEMAGE.." "..age);
			local numlines=GameTooltip:NumLines();
			local thetext=getglobal("GameTooltipTextLeft"..numlines);
			thetext:SetTextColor(1,1,1);
			GameTooltip:SetHeight(GameTooltip:GetHeight()+thetext:GetHeight());
			local width=thetext:GetWidth();
			if (width>GameTooltip:GetWidth()) then GameTooltip:SetWidth(width+25); end
--			GameTooltip:SetMinimumWidth(width+25);
		end
	end
end


function DuckieBank_GetItemAge(link)
	link=DuckieBank_GetUniqueItem(link);
	if (not DuckieBank_LS.Age) then return nil; end
	if (not DuckieBank_LS.Age[link]) then return nil; end
	if (not DuckieBank_LS.Age[link].Time) then return nil; end
	return SecondsToTime(time()-DuckieBank_LS.Age[link].Time,true);
end


-- Option-check clicked
function DuckieBank_OptionsCheck_OnClick()
	if (this==DuckieBank_BankerFrame_Options_BankerRank) then
		DuckieBank_BankerFrame_Options_BankerONote:SetChecked(false);
		DuckieBank_BankerFrame_Options_BankerPNote:SetChecked(false);
	elseif (this==DuckieBank_BankerFrame_Options_BankerONote) then
		DuckieBank_BankerFrame_Options_BankerRank:SetChecked(false);
		DuckieBank_BankerFrame_Options_BankerPNote:SetChecked(false);
	elseif (this==DuckieBank_BankerFrame_Options_BankerPNote) then
		DuckieBank_BankerFrame_Options_BankerRank:SetChecked(false);
		DuckieBank_BankerFrame_Options_BankerONote:SetChecked(false);
	end
end


-- Banker-button clicked
function DuckieBank_Button_OnClick(arg1)
	local value=DuckieBank_Button_Selector();
	if (not value) then return; end;

	if (arg1=="LeftButton") then
		if (IsControlKeyDown()) then DressUpItemLink(value.link);												-- Dress-up
		elseif (ChatFrameEditBox:IsVisible() and IsShiftKeyDown()) then ChatFrameEditBox:Insert(value.link);	-- Link
		elseif (IsShiftKeyDown()) then																			-- Buy a stack
			DuckieBank_SetBuyItem(value,-1);
		else																									-- Buy one
			DuckieBank_SetBuyItem(value,1);
		end
	elseif (arg1=="RightButton") then
		if (not DuckieBank_Banker) then return; end;
		DuckieBank_EditCustomCost:Show();
		local text="";
		if (value.cost) then text=value.cost; end
		ResetCursor(); GameTooltip:Hide();
		DuckieBank_EditCustomCost:ClearAllPoints();
		DuckieBank_EditCustomCost:SetPoint("TOP",this,"TOP");
		DuckieBank_EditCustomCost:SetText(text);
		DuckieBank_EditCustomCost:SetFocus();
		DuckieBank_EditItem=DuckNet_CopyTable(value);
		DuckieBank_EditItem.Button=this;
		this:Hide();
	end
end


function DuckieBank_SetBuyItem(item,add)
	local itemID=item.link;
	if (not itemID) then
		DuckieBank_RequestItemIconTexture:SetTexture("");
		SetItemButtonCount(button,0);
		DuckieBank_RequestItem.id=nil;
		DuckieBank_RequestItem.stackcount=0;
		return;
	end

	if (not DuckieBank_RequestItem.id) then DuckieBank_RequestItem.id=itemID; DuckieBank_RequestItem.stackcount=0; end
	if (not DuckieBank_RequestItem.stackcount) then DuckieBank_RequestItem.stackcount=0; end
	if (DuckieBank_RequestItem.id==itemID) then DuckieBank_RequestItem.stackcount=DuckieBank_RequestItem.stackcount+add;
	else DuckieBank_RequestItem.id=itemID; DuckieBank_RequestItem.stackcount=1; end
	if (DuckieBank_RequestItem.stackcount<1) then DuckieBank_RequestItem.stackcount=1; end
	DuckieBank_RequestItemIconTexture:SetTexture(item.icon);
	SetItemButtonCount(DuckieBank_RequestItem,DuckieBank_RequestItem.stackcount);

	local name,_,rarity=GetItemInfo(item.link); 
	r,g,b=1,1,1;
	if (type(rarity)=="number" and rarity<7) then r,g,b=GetItemQualityColor(rarity); end				-- Further test for uncached items
	DuckieBank_RequestItemName:SetText(name);
	DuckieBank_RequestItemName:SetTextColor(r,g,b);
end


-- tp -> player
-- tp -> empty slot
function DuckieBank_ValidateRequest(banker,onlyplayer,generate)
	if (not DuckieBank_LastGuild.Requests) then DuckieBank_LastGuild.Requests={}; end
	if (not DuckieBank_LastGuild.Requests[banker]) then DuckieBank_LastGuild.Requests[banker]={}; end
	if (not generate) then generate=UnitName("player"); end
	if (not DuckieBank_LastGuild.Requests[banker][generate]) then DuckieBank_LastGuild.Requests[banker][generate]={}; end
	if (onlyplayer) then return DuckieBank_LastGuild.Requests[banker][generate]; end
	local seq=1; while(DuckieBank_LastGuild.Requests[banker][generate][seq]) do seq=seq+1; end
	DuckieBank_LastGuild.Requests[banker][generate][seq]={};
	return DuckieBank_LastGuild.Requests[banker][generate][seq];
end


function DuckieBank_ShowRequestFrame()
	DuckieBank_CleanupRequests();
	DuckieBank_BankerFrame_RequestsItem1IconTexture:SetTexture("");
	DuckieBank_BankerFrame_RequestsItem1Item:SetText(""); DuckieBank_BankerFrame_RequestsItem1Player:SetText(""); DuckieBank_BankerFrame_RequestsItem1Notes:SetText("");
	DuckieBank_BankerFrame_RequestsItem1Remove.Item=nil;
	DuckieBank_BankerFrame_RequestsItem2IconTexture:SetTexture("");
	DuckieBank_BankerFrame_RequestsItem2Item:SetText(""); DuckieBank_BankerFrame_RequestsItem2Player:SetText(""); DuckieBank_BankerFrame_RequestsItem2Notes:SetText("");
	DuckieBank_BankerFrame_RequestsItem2Remove.Item=nil;
	DuckieBank_BankerFrame_RequestsItem3IconTexture:SetTexture("");
	DuckieBank_BankerFrame_RequestsItem3Item:SetText(""); DuckieBank_BankerFrame_RequestsItem3Player:SetText(""); DuckieBank_BankerFrame_RequestsItem3Notes:SetText("");
	DuckieBank_BankerFrame_RequestsItem3Remove.Item=nil;
	DuckieBank_BankerFrame_RequestsItem4IconTexture:SetTexture("");
	DuckieBank_BankerFrame_RequestsItem4Item:SetText(""); DuckieBank_BankerFrame_RequestsItem4Player:SetText(""); DuckieBank_BankerFrame_RequestsItem4Notes:SetText("");
	DuckieBank_BankerFrame_RequestsItem4Remove.Item=nil;
	DuckieBank_UpdateRequests();
end


function DuckieBank_BuildRequestList()
	if (not DuckieBank_CurrentRequestTable) then return; end
	DuckNet_ClearTable(DuckieBank_RL);
	local count=0;
	local seq;
	for player,pt in pairs(DuckieBank_CurrentRequestTable) do
		seq=1;
		while(pt[seq]) do
			if (not pt[seq].Removed) then
				count=count+1;
				DuckieBank_RL[count]={};
				DuckieBank_RL[count].Player=player;
				DuckieBank_RL[count].itemID=pt[seq].itemID;
				DuckieBank_RL[count].Count=pt[seq].Count;
				DuckieBank_RL[count].Stamp=pt[seq].Stamp;
				DuckieBank_RL[count].Notes=pt[seq].Notes;
			end
			seq=seq+1;
		end
	end

	seq=1;
	while(DuckieBank_RL[seq+1]) do
		local pos=seq;
		while (DuckieBank_RL[pos] and DuckieBank_RL[pos].Stamp>DuckieBank_RL[pos+1].Stamp) do
			local temp=DuckNet_CopyTable(DuckieBank_RL[pos]);
			DuckieBank_RL[pos]=DuckNet_CopyTable(DuckieBank_RL[pos+1]);
			DuckieBank_RL[pos+1]=DuckNet_CopyTable(temp);
			pos=pos-1;
		end
		seq=seq+1;
	end
end


function DuckieBank_UpdateRequests()
	if (not DuckieBank_Banker) then return; end
	DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return; end
	if (not DuckieBank_LastGuild.Requests) then return; end;
	local banker=UnitName("player");
	if (not DuckieBank_LastGuild.Requests[banker]) then return; end
	-- The slider setup
	local slidemax=DuckieBank_CountRequest(DuckieBank_LastGuild.Requests[banker])-4;
	if (slidemax<0) then slidemax=0; end
	DuckieBank_BankerFrame_RequestsSlider:SetMinMaxValues(0,slidemax);
	-- Draw
	DuckieBank_CurrentRequestTable=DuckieBank_LastGuild.Requests[banker];
	DuckieBank_BuildRequestList();
	DuckieBank_RedrawRequests();
end


function DuckieBank_RedrawRequests()
	if (not DuckieBank_CurrentRequestTable) then return; end
	local offset=DuckieBank_BankerFrame_RequestsSlider:GetValue();
	local count=0;
	local index=1;

	local seq=offset+1;
	while(DuckieBank_RL[seq]) do
		local name,_,rarity,_,_,itemType,itemSubType,_,_,Texture=GetItemInfo(DuckieBank_RL[seq].itemID); 
		r,g,b=1,1,1;
		if (type(rarity)=="number" and rarity<7) then r,g,b=GetItemQualityColor(rarity); end				-- Further test for uncached items
		getglobal("DuckieBank_BankerFrame_RequestsItem"..index.."IconTexture"):SetTexture(Texture);
		SetItemButtonCount(getglobal("DuckieBank_BankerFrame_RequestsItem"..index),DuckieBank_RL[seq].Count);
		getglobal("DuckieBank_BankerFrame_RequestsItem"..index.."Item"):SetText(name);
		getglobal("DuckieBank_BankerFrame_RequestsItem"..index.."Item"):SetTextColor(r,g,b);
		getglobal("DuckieBank_BankerFrame_RequestsItem"..index.."Player"):SetText(DuckieBank_RL[seq].Player.." ("..date("%a, %d. %b",DuckieBank_RL[seq].Stamp)..")");
		getglobal("DuckieBank_BankerFrame_RequestsItem"..index.."Notes"):SetText(DuckieBank_RL[seq].Notes);
		getglobal("DuckieBank_BankerFrame_RequestsItem"..index.."Remove").Item=seq;
		if (index==4) then return; end
		index=index+1;
		seq=seq+1;
	end
	if (index<5) then
		DuckieBank_BankerFrame_RequestsItem4IconTexture:SetTexture(""); DuckieBank_BankerFrame_RequestsItem4Remove.Item=nil;
		DuckieBank_BankerFrame_RequestsItem4Item:SetText(""); DuckieBank_BankerFrame_RequestsItem4Player:SetText(""); DuckieBank_BankerFrame_RequestsItem4Notes:SetText("");
		SetItemButtonCount(DuckieBank_BankerFrame_RequestsItem4,0);
	end
	if (index<4) then
		DuckieBank_BankerFrame_RequestsItem3IconTexture:SetTexture(""); DuckieBank_BankerFrame_RequestsItem3Remove.Item=nil;
		DuckieBank_BankerFrame_RequestsItem3Item:SetText(""); DuckieBank_BankerFrame_RequestsItem3Player:SetText(""); DuckieBank_BankerFrame_RequestsItem3Notes:SetText("");
		SetItemButtonCount(DuckieBank_BankerFrame_RequestsItem3,0);
	end
	if (index<3) then
		DuckieBank_BankerFrame_RequestsItem2IconTexture:SetTexture(""); DuckieBank_BankerFrame_RequestsItem2Remove.Item=nil;
		DuckieBank_BankerFrame_RequestsItem2Item:SetText(""); DuckieBank_BankerFrame_RequestsItem2Player:SetText(""); DuckieBank_BankerFrame_RequestsItem2Notes:SetText("");
		SetItemButtonCount(DuckieBank_BankerFrame_RequestsItem2,0);
	end
	if (index<2) then
		DuckieBank_BankerFrame_RequestsItem1IconTexture:SetTexture(""); DuckieBank_BankerFrame_RequestsItem1Remove.Item=nil;
		DuckieBank_BankerFrame_RequestsItem1Item:SetText(""); DuckieBank_BankerFrame_RequestsItem1Player:SetText(""); DuckieBank_BankerFrame_RequestsItem1Notes:SetText("");
		SetItemButtonCount(DuckieBank_BankerFrame_RequestsItem1,0);
	end
end


function DuckieBank_RemoveRequestClick()
	if (not this.Item) then return; end

	local pt=DuckieBank_CurrentRequestTable[DuckieBank_RL[this.Item].Player];
	local seq=1;
	while(pt[seq]) do
		if (not pt[seq].Removed) then
			if (pt[seq].itemID==DuckieBank_RL[this.Item].itemID and pt[seq].Count==DuckieBank_RL[this.Item].Count and pt[seq].Stamp==DuckieBank_RL[this.Item].Stamp) then
				pt[seq].Removed=true;
			end
		end
		seq=seq+1;
	end

	DuckieBank_ShowRequestFrame();
end


function DuckieBank_MakeRequest()
	DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return; end

	local dropframe=DuckieBank_BankerSelectDropDown;
	if (not dropframe) then return;
	elseif (not dropframe.selectedID) then UIDropDownMenu_SetSelectedID(dropframe,1); if (not dropframe.selectedID) then return; end
	end
	if (not DuckieBank_LastGuild[dropframe.selectedID]) then return; end;
	if (not DuckieBank_LastGuild[dropframe.selectedID].Banker) then return; end;

	if (not DuckieBank_RequestItem.id or not DuckieBank_RequestItem.stackcount or DuckieBank_RequestItem.stackcount<1) then return; end

	-- Save entry
	local now=time();
	local entry=DuckieBank_ValidateRequest(DuckieBank_LastGuild[dropframe.selectedID].Banker);		-- Make new request
	entry.itemID=DuckieBank_RequestItem.id;
	entry.Count=DuckieBank_RequestItem.stackcount;
	entry.Notes=DuckieBank_RequestNote:GetText();
	entry.Stamp=now;
	DuckieBank_NewRequests=true;
	DuckieBank_ValidateRequest(DuckieBank_LastGuild[dropframe.selectedID].Banker,true).Stamp=now;

	-- Clear contents
	DuckieBank_RequestItem.id=nil;
	DuckieBank_RequestItem.stackcount=nil;
	DuckieBank_RequestItemIconTexture:SetTexture("");
	SetItemButtonCount(DuckieBank_RequestItem,1);
	DuckieBank_RequestNote:SetText("");
	DuckieBank_RequestItemName:SetText("");
end


function DuckieBank_CustomCostChanged()
	DuckieBank_EditCustomCost:Hide(); if (not DuckieBank_EditItem) then return; end;
	DuckieBank_EditItem.Button:Show();
	local index=DuckieBank_BankerIndex(UnitName("player")); if (not index) then return; end;
	index.Stamp=time();						-- Make new stamp
	local Cost=DuckieBank_BankerCost(); if (not Cost) then return; end;	-- Get cost-list

	DuckieBank_EditItem.cost=DuckieBank_EditCustomCost:GetText();
	local itemID=DuckieBank_GetUniqueItem(DuckieBank_EditItem.link);
	if (not Cost[itemID]) then Cost[itemID]={}; end
	Cost[itemID].Cost=DuckieBank_EditItem.cost;
	Cost[itemID].Type="Custom";

	DuckieBank_Timers.UpdateNotified=false;
	DuckieBank_BankerSend=UnitName("player");
	DuckieBank_Timers.NextSend=DBT_INACTIVITY_SEND;			-- Set send timer
	DuckieBank_StuffBankItems();
end


function DuckieBank_CustomCostUnchanged()
	DuckieBank_EditCustomCost:Hide();
	if (not DuckieBank_EditItem) then return; end;
	DuckieBank_EditItem.Button:Show();
end


-- Generic chat-pane stuff
function DuckieBank_Chat(msg,r,g,b)
	if (DEFAULT_CHAT_FRAME) then
		if (not r and not g and not b) then r=1; g=0; b=1; end;
		if (not r) then r=0; end;
		if (not g) then g=0; end;
		if (not b) then b=0; end;
		DEFAULT_CHAT_FRAME:AddMessage("DuckieBank: "..msg,r,g,b);
	end
end

-- Duh
function DuckieBank_ToggleMainFrame()
	if (DuckieBank_Ready~=true) then return; end;
	if (DuckieBank_AFrame:IsVisible()) then
		DuckieBank_AFrame:Hide();
	else
		DuckieBank_StuffBankItems();
		DuckieBank_AFrame:Show();
	end
end
 
-- Open Main frame from slash command
function DuckieBank_OpenMainFrame()
		DuckieBank_StuffBankItems();
		DuckieBank_AFrame:Show();
end

-- Force a transmission of my bank-stuff
function DuckieBank_SendBankerButtonClick()
	if (not DuckieBank_Banker) then return; end;
	local index=DuckieBank_BankerIndex(UnitName("player")); if (not index) then return; end;
	index.Stamp=time();						-- Make new stamp
	DuckieBank_Timers.UpdateNotified=true;
	DuckieBank_BankerSend=UnitName("player");
	DuckieBank_Timers.NextPoll=DBT_INTERVAL_POLL;			-- Restart poll-timer
	DuckieBank_Timers.NextSend=DBT_INACTIVITY_NOTIFY;		-- Set send timer
	DuckieBank_TransmitPressed=true;						-- Interactive transmission
	DuckieBank_Chat(DUCKIEBANK_NOTIFY_PREPARESEND);
end

-- Send a banker
function DuckieBank_SendBank(banker)
	DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return nil; end

	local index=DuckieBank_BankerIndex(banker); if (not index) then return nil; end
	local ThisBanker=DuckieBank_BankerItems(banker); if (not ThisBanker) then return nil; end

	-- Header
	if (not DuckNet_CanTransmit(DUCKIEBANK_PREFIX)) then if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: DuckNet is not ready for transmission"); end return nil; end
	DuckNet_ClearOutput(DUCKIEBANK_PREFIX);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"S",index.Stamp);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"M",index.Banker);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"V",index.VerData);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"D",index.VerBanker);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"T",DUCKIEBANK_VERSION_BANKNUM);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"A",DUCKNET_ACT_TRANSMIT);

	if (index.Notes) then
		local splitlines=index.Notes;
		while(string.find(splitlines,"\n")) do
			if (splitlines=="\n") then break; end;
			local pos=string.find(splitlines,"\n");
			local text;
			if (pos==1) then text=" ";
			else text=string.sub(splitlines,1,pos-1); end
			splitlines=string.sub(splitlines,pos+1);
			DuckNet_NewLine(DUCKIEBANK_PREFIX);
			DuckNet_AddKey(DUCKIEBANK_PREFIX,"N",text);
		end
		if (splitlines~="\n" and string.len(splitlines)>0) then
			DuckNet_NewLine(DUCKIEBANK_PREFIX);
			DuckNet_AddKey(DUCKIEBANK_PREFIX,"N",splitlines);
		end
	end

	-- Do all bank slots
	local seq=1;
	while (ThisBanker[seq]) do
		local tp=ThisBanker[seq];
		if (tp["link"]) then
			DuckNet_NewLine(DUCKIEBANK_PREFIX);
			DuckNet_AddTable(DUCKIEBANK_PREFIX,seq);
			DuckNet_AddEntry(DUCKIEBANK_PREFIX,"icon",tp["icon"]);
			DuckNet_AddEntry(DUCKIEBANK_PREFIX,"count",tp["count"]);
			DuckNet_AddEntry(DUCKIEBANK_PREFIX,"link",tp["link"]);

			if (not DuckieBank_Banker or banker~=UnitName("player")) then							-- Not me, so use registry
				if (tp["cost"]) then DuckNet_AddEntry(DUCKIEBANK_PREFIX,"cost",tp["cost"]); end
			else																					-- Me, so use my list
				DuckNet_AddEntry(DUCKIEBANK_PREFIX,"cost",DuckieBank_GetItemLocalCost(tp));
			end
		end
		seq=seq+1;
	end

	if (DuckieBank_LS.ShowMoney and index.Money and index.Money>=0) then
		DuckNet_NewLine(DUCKIEBANK_PREFIX);
		DuckNet_AddEntry(DUCKIEBANK_PREFIX,"BankerMoney",index.Money);
	end

	-- Footer
	if (DuckieBank_LS.Debug and not DuckieBank_Banker) then DuckieBank_Chat("DEBUG: Sending...");
	elseif (DuckieBank_Banker) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_SENDING); end
	DuckNet_DoTransmission(DUCKIEBANK_PREFIX);
	
	-- Queue settings
	DuckieBank_SendSettings();
	return true;
end


-- Read through a request-tree
function DuckieBank_CleanupRequests(input)
	if (not input) then input=DuckieBank_LastGuild.Requests; end
	if (not input) then return; end
	local now=time();
	for banker,bTable in pairs(input) do						-- All bankers
		local count=0;
		for player,pTable in pairs(bTable) do					-- All players
			if (not pTable.Stamp or type(pTable.Stamp)~="number" or now-pTable.Stamp>DBT_REQUEST_DEATH) then
				bTable[player]=nil;
			else
				count=count+1;
				local seq=1; while(pTable[seq]) do
					if (not pTable[seq].Stamp or (now-pTable[seq].Stamp>DBT_REQUEST_DEATH)) then
						local move=seq; while(pTable[move+1]) do pTable[move]=DuckNet_CopyTable(pTable[move+1]); move=move+1; end
						pTable[move]=nil;													-- Kill request
					else
						seq=seq+1;
					end
				end
			end
		end
		if (count<1) then input[banker]=nil; end
	end
end


function DuckieBank_SendSettings()
	DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return nil; end
	if (not DuckieBank_LastGuild[1].Settings) then return nil; end								-- No settings to send
	if (not DuckieBank_LastGuild[1].Settings.Stamp) then return nil; end						-- No stamp

	-- Header
	if (not DuckNet_CanTransmit(DUCKIEBANK_PREFIX)) then
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: DuckNet is not ready to send \"Settings\". Retrying..."); end
		DuckieBank_Timers.BumpSendSettings=math.random(7,15);
		return DBV_RETRY;
	end

	DuckNet_ClearOutput(DUCKIEBANK_PREFIX);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"M",DUCKIEBANK_SETTING);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"T",DUCKIEBANK_VERSION_BANKNUM);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"A",DUCKNET_ACT_TRANSMIT);

	local entries=0;
	local seq=1;
	while (DuckieBank_LastGuild[seq] and DuckieBank_LastGuild[seq].Banker) do
		if (DuckieBank_LastGuild[seq].Settings) then
			for eName,eData in pairs(DuckieBank_LastGuild[index].Settings) do
				DuckNet_NewLine(DUCKIEBANK_PREFIX);
				DuckNet_AddTable(DUCKIEBANK_PREFIX,DuckieBank_LastGuild[seq].Banker);
				DuckNet_AddEntry(DUCKIEBANK_PREFIX,eName,eData);
				entries=entries+1;
			end
		end
		seq=seq+1;
	end

	if (entries<1) then
		DuckNet_ClearOutput(DUCKIEBANK_PREFIX);
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: No settings to send"); end
		return DBV_ABORT;
	end

	-- Footer
	if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Sending settings..."); end
	DuckNet_DoTransmission(DUCKIEBANK_PREFIX);
	return DBV_OKAY;
end


function DuckieBank_SendRequests()
	DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return DBV_ABORT; end
	if (not DuckieBank_LastGuild.Requests) then return DBV_ABORT; end

	-- Header
	if (not DuckNet_CanTransmit(DUCKIEBANK_PREFIX)) then
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: DuckNet is not ready to send \"Requests\". Retrying..."); end
		DuckieBank_Timers.BumpSendRequests=math.random(7,15);
		return DBV_RETRY;
	end
	DuckNet_ClearOutput(DUCKIEBANK_PREFIX);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"M",DUCKIEBANK_REQUEST);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"T",DUCKIEBANK_VERSION_BANKNUM);
	DuckNet_AddKey(DUCKIEBANK_PREFIX,"A",DUCKNET_ACT_TRANSMIT);

	local entries=0;
	-- Build all requests
	for banker,bTable in pairs(DuckieBank_LastGuild.Requests) do			-- All requests
		for player,pTable in pairs(bTable) do								-- All bankers
			DuckNet_NewLine(DUCKIEBANK_PREFIX);
			DuckNet_AddTable(DUCKIEBANK_PREFIX,banker);
			DuckNet_AddTable(DUCKIEBANK_PREFIX,player);
			DuckNet_AddEntry(DUCKIEBANK_PREFIX,"Stamp",pTable.Stamp);
			local seq=1; while(pTable[seq]) do
				DuckNet_NewLine(DUCKIEBANK_PREFIX);
				DuckNet_AddTable(DUCKIEBANK_PREFIX,banker);
				DuckNet_AddTable(DUCKIEBANK_PREFIX,player);
				DuckNet_AddTable(DUCKIEBANK_PREFIX,seq);
				DuckNet_AddEntry(DUCKIEBANK_PREFIX,"itemID",pTable[seq].itemID);
				DuckNet_AddEntry(DUCKIEBANK_PREFIX,"Count",pTable[seq].Count);
				DuckNet_AddEntry(DUCKIEBANK_PREFIX,"Notes",pTable[seq].Notes);
				DuckNet_AddEntry(DUCKIEBANK_PREFIX,"Stamp",pTable[seq].Stamp);
				entries=entries+1;
				seq=seq+1;
			end
		end
	end
	
	DuckieBank_NewRequests=false;
	if (entries<1) then
		DuckNet_ClearOutput(DUCKIEBANK_PREFIX);
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: No requests to send"); end
		return DBV_ABORT;
	end

	-- Footer
	if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Sending requests..."); end
	DuckNet_DoTransmission(DUCKIEBANK_PREFIX);
	return DBV_OKAY;
end


function DuckieBank_CollateMyRequests()
	if (not DuckieBank_Banker) then return; end
	DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return; end

	-- Check idle
	if (not DuckNet_Idle(DUCKIEBANK_PREFIX)) then
		DuckieBank_Chat(" ==========> "..DUCKIEBANK_WARNING_NETBUSY);
		DuckieBank_Timers.CollateBump=math.random(7,15);
		return
	end

	-- Just in case...
	if (DuckNet_Poll(DUCKIEBANK_PREFIX,0,DUCKIEBANK_COLLATE,DUCKIEBANK_VERSION_BANKNUM)==false) then
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: DuckNet is not ready for polling. Retrying banker collate...");
		else DuckieBank_Chat(" ==========> "..DUCKIEBANK_WARNING_NETBUSY); end
		DuckieBank_Timers.CollateBump=math.random(7,15);
		return;
	end

	if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Collate-event started");
	else DuckieBank_Chat(DUCKIEBANK_NOTIFY_CHECKINGREQUESTS); end
	DuckieBank_CollateEvent="Start";
end


--[[ Net-stuff ]]
function DuckieBank_Connect(silent)
	if (not DuckNet_Register) then return false; end;
	if (not DUCKNET_VERSION_API or DUCKNET_VERSION_API<DUCKIEBANK_REQUIRED_DUCKNET) then
		if (DuckieBank_WrongDuckNet~=true) then
			DuckieBank_WrongDuckNet=true;
			DuckieBank_Chat("========>",1);
			DuckieBank_Chat("========> "..DUCKIEBANK_VERSIONTEXT.." "..DUCKIEBANK_ERROR_WRONGNET_EXTRA.." "..DUCKNET_VERSIONTEXT,1);
		end
		return false;
	end
	DuckNet_Register(DUCKIEBANK_PREFIX,"GUILD",DuckieBank_Callback_RX,DuckieBank_Callback_TX,DuckieBank_Callback_INFO,DuckieBank_Callback_CS,DuckieBank_Callback_NW,DuckieBank_Callback_IS,DuckieBank_AFramePB);
	DuckieBank_NetHook=DuckNet_GetHook();
	if (not silent) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_CONNECTED); end
	return true;
end


--[[ Support-functions ]]
function DuckieBank_ClearBags()
	DuckieBank_LastGuild=DuckieBank_TableSection();
	if (not DuckieBank_LastGuild) then return; end;

	-- Clear here

	DuckieBank_LastGuild=DuckieBank_TableSection();
end

function DuckieBank_Numeric(typestring)
	if (typestring=="number" or typestring=="nil") then return true; end;
	return false;
end


function DuckieBank_GetGuildRosterInfo(toon)
	local i=1;
	local name=GetGuildRosterInfo(i);
	while (name and name~=toon) do
		i=i+1;
		name=GetGuildRosterInfo(i);
	end
	if (name==toon) then return GetGuildRosterInfo(i); end
end


function DuckieBank_BankerState()
	dummy,rank,_,_,_,_,pnote,onote=DuckieBank_GetGuildRosterInfo(UnitName("player"));
	if (not dummy or not rank) then return nil; end
	if (rank=="") then return nil; end

	local note="";
	if (onote and string.find(onote,"#DB:")) then
		note=onote;
	end

	-- Test rank
	if (DuckieBank_LS.DebugServer==true) then DuckieBank_Chat("Server: Rank \""..rank.."\" found"); end
	rank=string.lower(rank);
	if (rank==DB_BANKERRANK) then return "Banker"; end

	-- Test guild note
	if (string.find(note,"#DB:")) then
		if (string.find(note,"#DB:B")) then
			if (DuckieBank_LS.DebugServer==true) then DuckieBank_Chat("Server: Banker setting in officer's note found"); end
			return "Banker";
		end
		if (string.find(note,"#DB:M")) then
			if (DuckieBank_LS.DebugServer==true) then DuckieBank_Chat("Server: Monitor-setting in officer's note found"); end
			return "Monitor";
		end
	end

	-- All tests failed
	return "User";
end

-- Find the correct guild and pointer
function DuckieBank_TableSection()
	if (DuckieBank_LastGuild) then return DuckieBank_LastGuild; end			-- To not bug the server more than need be
	local section=GetGuildInfo("player"); if (not section) then return nil; end
	local realm=GetRealmName(); if (not realm) then return nil; end
	DuckieBank_ATitleText:SetText(section.." "..DUCKIEBANK_TEXT_GUILDBANK);
	if (DuckieBank_LS.DebugServer==true) then DuckieBank_Chat("Server: Guildname \""..section.."\" found"); DuckieBank_Chat("Server: Realm \""..realm.."\" found"); end
	newdb=section.." - "..realm;
	if (DuckieBank_DB[section]) then DuckieBank_DB[newdb]=DuckNet_CopyTable(DuckieBank_DB[section]); DuckieBank_DB[section]=nil; end
	if (not DuckieBank_DB[newdb] or table.maxn(DuckieBank_DB[newdb])==0) then DuckieBank_DB[newdb]={}; end
	return DuckieBank_DB[newdb];
end

-- (Re)build the dropdownbox
function DuckieBank_PopulateDD()
	UIDropDownMenu_Initialize(DuckieBank_BankerSelectDropDown,DuckieBank_BankerDD_Init);
end
-- (Re)build the dropdownbox
function DuckieBank_PopulateFilterDD()
	UIDropDownMenu_Initialize(DuckieBank_FilterSelectDropDown,DuckieBank_FilterDD_Init);
	UIDropDownMenu_SetSelectedID(DuckieBank_FilterSelectDropDown,1);
end

-- Kill a banker
function DuckieBank_RemoveBanker(entry,silent)
	if (not DuckieBank_LastGuild[entry]) then return; end;
	local thisname=DuckieBank_LastGuild[entry].Banker;

	while (DuckieBank_LastGuild[entry+1]) do
		DuckieBank_LastGuild[entry]=DuckNet_CopyTable(DuckieBank_LastGuild[entry+1]);
		entry=entry+1;
	end

	-- Ye ye... Superfluous and all that, but tables has been changed in LUA already,
	-- and I'm gonna continue to distrust the consistency.
	DuckNet_ClearTable(DuckieBank_LastGuild[entry]); DuckieBank_LastGuild[entry]=nil;
	if (DuckieBank_LastGuild[thisname]) then
		DuckNet_ClearTable(DuckieBank_LastGuild[thisname]); DuckieBank_LastGuild[thisname]=nil;
	end
	DuckieBank_PopulateDD();
	
	if (not silent) then DuckieBank_Chat(string.format(DUCKIEBANK_NOTIFY_OLDBANKER,thisname));
	elseif (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Banker "..thisname.." has been deleted"); end
end


function DuckieBank_FilterName(index)
	if (index==2) then return DBT_ARMOR; end
	if (index==3) then return DBT_CONSUMABLE; end
	if (index==4) then return DBT_CONTAINER; end
	if (index==5) then return DBT_REAGENT; end
	if (index==6) then return DBT_RECIPE; end
	if (index==7) then return DBT_PROJECTILE; end
	if (index==8) then return DBT_QUEST; end
	if (index==9) then return DBT_QUIVER; end
	if (index==10) then return DBT_TRADEGOODS; end
	if (index==11) then return DBT_WEAPON; end
	return DUCKIEBANK_TEXT_SHOWALL;
end


-- Populate filter dropdown
function DuckieBank_FilterDD_Init()
	info = {};
	info.text=DuckieBank_FilterName(1);
	info.checked=nil;
	info.func = DuckieBank_FilterDD_OnClick;
	UIDropDownMenu_AddButton(info);
	if (not DuckieBank_DataCache) then return; end

	info.text=DuckieBank_FilterName(2); info.checked=nil; UIDropDownMenu_AddButton(info);
	info.text=DuckieBank_FilterName(3); info.checked=nil; UIDropDownMenu_AddButton(info);
	info.text=DuckieBank_FilterName(4); info.checked=nil; UIDropDownMenu_AddButton(info);
	info.text=DuckieBank_FilterName(5); info.checked=nil; UIDropDownMenu_AddButton(info);
	info.text=DuckieBank_FilterName(6); info.checked=nil; UIDropDownMenu_AddButton(info);
	info.text=DuckieBank_FilterName(7); info.checked=nil; UIDropDownMenu_AddButton(info);
	info.text=DuckieBank_FilterName(8); info.checked=nil; UIDropDownMenu_AddButton(info);
	info.text=DuckieBank_FilterName(9); info.checked=nil; UIDropDownMenu_AddButton(info);
	info.text=DuckieBank_FilterName(10); info.checked=nil; UIDropDownMenu_AddButton(info);
	info.text=DuckieBank_FilterName(11); info.checked=nil; UIDropDownMenu_AddButton(info);
end


function DuckieBank_FilterDD_OnClick()
	UIDropDownMenu_SetSelectedID(DuckieBank_FilterSelectDropDown,this:GetID());
	if (not DuckieBank_FilterSelectDropDown.selectedID) then return; end
	DuckieBank_StuffBankItems();
end


-- Populate banker dropdown
function DuckieBank_BankerDD_Init()
	local i=1;
	local info = {};
	if (not DuckieBank_LastGuild) then
		info = { text = "<"..DUCKIEBANK_TEXT_NOBANKERSYET..">"; func = DuckieBank_BankerDD_OnClick; };
		UIDropDownMenu_AddButton(info);
		return;
	end;

	while (DuckieBank_LastGuild[i]) do
		if (not DuckieBank_LastGuild[i].Banker) then break; end;	-- No more
		info = {};
		info.text = DuckieBank_LastGuild[i].Banker;
		info.checked=nil; 
		info.func = DuckieBank_BankerDD_OnClick;
		UIDropDownMenu_AddButton(info);
		i=i+1;
	end
	
	if (i==1) then
		info = { text = "<"..DUCKIEBANK_TEXT_NOBANKERSYET..">"; func = DuckieBank_BankerDD_OnClick; };
		UIDropDownMenu_AddButton(info);
	end
end


-- A (new) banker has been selected
function DuckieBank_BankerDD_OnClick()
	UIDropDownMenu_SetSelectedID(DuckieBank_BankerSelectDropDown,this:GetID());
	DuckieBank_StuffBankItems();
end


-- Total traversion
function DuckieBank_CountRequest(tp)
	local active=0;
	for player,pTable in pairs(tp) do										-- Each of the players
		local seq=1;
		while(pTable[seq]) do
			if (not pTable[seq].Removed) then active=active+1; end
			seq=seq+1;
		end
	end
	return active;
end


function DuckieBank_CountRequestBlocks()
	DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return 0; end
	if (not DuckieBank_LastGuild.Requests) then return 0; end

	local count=0;
	for player,bTable in pairs(DuckieBank_LastGuild.Requests) do	-- Each banker
		for player,pTable in pairs(bTable) do						-- Each player in banker
			count=count+1;
		end
	end
	return count;
end


function DuckieBank_GetRequestBlockInfo(index)
	DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return "","",0; end
	if (not DuckieBank_LastGuild.Requests) then return "","",0; end

	local count=0;
	for banker,bTable in pairs(DuckieBank_LastGuild.Requests) do				-- Each banker
		for player,pTable in pairs(bTable) do									-- Each player in banker
			count=count+1;
			if (count==index) then return banker,player,pTable.Stamp; end		-- Get this info
		end
	end
	return "","",0;
end


function DuckieBank_CountBankers()
	DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return 0; end

	local seq=1;
	while(DuckieBank_LastGuild[seq]) do seq=seq+1; end
	return seq-1;
end



--[[                        ]]
--[[    DuckNet callback    ]]
--[[                        ]]

-- input - timestamp for last data received for this addon
function DuckieBank_Callback_IS(instamp)
	DuckieBank_Timers.LastInStamp=instamp;
	DuckieBank_BumpPoll();
end


-- Returns "true" if the item was added/updated
function DuckieBank_OverlayItem(banker,player,item)
	local seq=1;
	local tp=DuckieBank_LastGuild.Requests[banker][player][seq];
	while(tp) do
		if (tp.Stamp==item.Stamp and tp.itemID==item.itemID and tp.Count==item.Count) then return nil; end		-- It's already here
		seq=seq+1;
		tp=DuckieBank_LastGuild.Requests[banker][player][seq];
	end
	DuckieBank_LastGuild.Requests[banker][player][seq]=DuckNet_CopyTable(item);
	return true;
end


-- input - A DuckieBank table
function DuckieBank_Callback_RX(input)
	if (not DuckieBank_Header.Banker) then
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: No banker in header @ Callback_RX"); end
		return;
	end

	if (DuckieBank_Header.Banker==DUCKIEBANK_REQUEST) then
		if (DuckieBank_Banker) then
			if (DuckieBank_CollateEvent=="Start") then		-- I'm a banker, and I started a collate sequence
				DuckieBank_CollateEvent="Running";			-- It's running, so it will traverse all known requests
				DuckieBank_Timers.NextPoll=5;				-- Start now-ish
			end
		end
		DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return; end
		if (not DuckieBank_LastGuild.Requests) then DuckieBank_LastGuild.Requests={}; end
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Requests received. Overlay in progress..."); end
		DuckieBank_CleanupRequests();						-- Local data
		DuckieBank_CleanupRequests(input);					-- Incoming data
		local newstuff=false;
		for banker,bTable in pairs(input) do														-- Each received banker
			local bankernews=false;
			if (not DuckieBank_NewStuff[banker]) then DuckieBank_NewStuff[banker]=0; end
			if (not DuckieBank_LastGuild.Requests[banker]) then										-- No requests for this banker
				DuckieBank_LastGuild.Requests[banker]=DuckNet_CopyTable(bTable);					-- Copy everything
				bankernews=true;
				DuckieBank_NewStuff[banker]=DuckieBank_NewStuff[banker]+1;							-- Unknown, so add only one
			else
				for player,pTable in pairs(bTable) do												-- Each received player
					if (not DuckieBank_LastGuild.Requests[banker][player]) then						-- Got none for this player
						DuckieBank_LastGuild.Requests[banker][player]=DuckNet_CopyTable(pTable);	-- Copy everything
							bankernews=true;
				            DuckieBank_NewStuff[banker]=DuckieBank_NewStuff[banker]+1;				-- Unknown, so add only one
                    
                    else
						if (DuckieBank_LastGuild.Requests[banker][player].Stamp<pTable.Stamp) then	-- Update final stamp
							DuckieBank_LastGuild.Requests[banker][player].Stamp=pTable.Stamp;
						end
						local seq=1; while(pTable[seq]) do											-- All received items
							if (DuckieBank_OverlayItem(banker,player,pTable[seq])) then
								bankernews=true;
								DuckieBank_NewStuff[banker]=DuckieBank_NewStuff[banker]+1;
							end
							seq=seq+1;
						end
					end
				end
			end
			if (DuckieBank_Monitor and bankernews) then
				DuckieBank_Chat(string.format(DUCKIEBANK_NOTIFY_NEWREQUESTSBANKER,banker));
				newstuff=true;
				bankernews=false;
			end
		end
		if (DuckieBank_BankerFrame_Requests:IsVisible()) then DuckieBank_ShowRequestFrame(); end
		if (DuckieBank_Monitor and newstuff) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NEWREQUESTS);
		elseif (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Item-requests updated"); end
		return;
	elseif (DuckieBank_Header.Banker==DUCKIEBANK_SETTING) then
		DuckieBank_LastGuild=DuckieBank_TableSection(); if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return; end
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Settings received. Overlay in progress..."); end
		for banker,bTable in pairs(input) do
			local index=DuckieBank_BankerIndex(banker);
			if (index) then
				if (not DuckieBank_LastGuild[index].Settings) then DuckieBank_LastGuild[index].Settings={}; end
				local tp=DuckieBank_LastGuild[index].Settings;
				if (bTable.Stamp) then bTable.Stamp=tonumber(bTable.Stamp); end
				if ((bTable.Stamp and tp.Stamp and tp.Stamp<bTable.Stamp) or not tp.Stamp) then			-- Newer stamp supplied or no stamp locally
					for eName,eData in pairs(bTable) do DuckieBank_LastGuild[index].Settings[eName]=eData; end
				end
			end
		end
		return;
	elseif (DuckieBank_Header.Banker==DUCKIEBANK_MARKER1) then return;
	elseif (DuckieBank_Header.Banker==DUCKIEBANK_MARKER2) then return;
	elseif (DuckieBank_Header.Banker==DUCKIEBANK_MARKER3) then return;
	elseif (DuckieBank_Header.Banker==DUCKIEBANK_MARKER4) then return;
	elseif (DuckieBank_Header.Banker==DUCKIEBANK_MARKER5) then return;
	elseif (DuckieBank_Header.Banker==DUCKIEBANK_MARKER6) then return;
	elseif (DuckieBank_Header.Banker==DUCKIEBANK_MARKER7) then return;
	elseif (DuckieBank_Header.Banker==DUCKIEBANK_MARKER8) then return;
	elseif (DuckieBank_Header.Banker==DUCKIEBANK_MARKER9) then return;
	end

	if (string.find(DuckieBank_Header.Banker,",")) then return; end		-- Erroneous compound statement

	local index=DuckieBank_BankerIndex(DuckieBank_Header.Banker,true);
	local store=false;
	if (not index.Stamp) then store=true;
	elseif (DuckieBank_Header.Stamp>index.Stamp) then store=true;
	elseif (DuckieBank_LS.Debug and DuckieBank_Header.Stamp==index.Stamp) then store=true;
	end
	if (not store) then
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Data not stored"); end
		return;
	end

	if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Storing data..."); end
	index.Banker=DuckieBank_Header.Banker;
	index.Stamp=DuckieBank_Header.Stamp;
	index.VerBanker=DuckieBank_Header.VerBanker;
	index.VerData=DuckieBank_Header.VerData;
	index.Notes=DuckieBank_Header.Notes;
	DuckieBank_Header.Notes="";

	local items=DuckieBank_BankerItems(DuckieBank_Header.Banker);			-- Get pointer to this banker
	if (not items) then
		if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Banker database not found"); end
		return;
	end

	DuckieBank_BankerDataCopyCopy(input,items);								-- Copy incoming to the banker
	index.Money=input.BankerMoney;
	DuckieBank_PopulateDD();

	DuckieBank_UpdateRoster();
	DuckieBank_Loader.index=1; DuckieBank_Loader.entry=1;		-- Start it
	DuckieBank_Chat(string.format(DUCKIEBANK_NOTIFY_NEWCONTENTS,DuckieBank_Header.Banker));
end


-- input - Transmitted lines
function DuckieBank_Callback_TX(input,origin)
	if (origin) then
		DuckieBank_Chat(DUCKIEBANK_NOTIFY_TRANSMITABORT_A.." "..origin);
		DuckieBank_Chat(DUCKIEBANK_NOTIFY_TRANSMITABORT_B);
	else
		if (DuckieBank_Banker and DuckieBank_TransmitPressed) then
			DuckieBank_Chat(DUCKIEBANK_NOTIFY_TRANSMITTED);
		elseif (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Transmitted");
		end
		DuckieBank_TransmitPressed=nil;
	end
end


-- input - A DuckNet table with information in the form of ordered tags and information
function DuckieBank_Callback_INFO(input)
	local info=input;
	local versionreceived1=0;
	local versionreceived2=0;

	local unknowntag=nil;
	local nothing=true;
	local gotmarker=nil;
	local gotstamp=nil;
	local collate=nil;

	local seq=1;
	while (info[seq]) do
		    if (info[seq].Type=="V") then DuckieBank_Header.VerData=tonumber(info[seq].Command); nothing=false;
		elseif (info[seq].Type=="D") then DuckieBank_Header.VerBanker=tonumber(info[seq].Command); versionreceived1=DuckieBank_Header.VerBanker; nothing=false;
		elseif (info[seq].Type=="T") then DuckieBank_Header.VerSender=tonumber(info[seq].Command); versionreceived2=DuckieBank_Header.VerSender; nothing=false;
		elseif (info[seq].Type=="M") then DuckieBank_Header.Banker=info[seq].Command; gotmarker=info[seq].Command; nothing=false;
			DuckieBank_Header.Notes=nil;
			if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Banker "..DuckieBank_Header.Banker.." stored in header"); end
		elseif (info[seq].Type=="S") then DuckieBank_Header.Stamp=tonumber(info[seq].Command); gotstamp=tonumber(info[seq].Command); nothing=false;
		elseif (info[seq].Type=="A") then nothing=false;
			if (DuckieBank_LS.Debug) then DuckieBank_Chat("DEBUG: Action "..info[seq].Command.." received"); end
		elseif (info[seq].Type=="N") then
			nothing=false;
			if (DuckieBank_Header.Notes) then DuckieBank_Header.Notes=DuckieBank_Header.Notes.."\n"; else DuckieBank_Header.Notes=""; end
			DuckieBank_Header.Notes=DuckieBank_Header.Notes..info[seq].Command;
		elseif (not unknowntag) then unknowntag=info[seq].Type;
		end
		seq=seq+1;
	end

	local first,second,third,fourth=DuckNet_SplitRequestMarker(gotmarker);
	-- Check for unknown banker
	if (gotmarker~=nil and gotstamp~=nil) then
		if (gotmarker==DUCKIEBANK_COLLATE) then							-- Some collation is ongoing
		elseif (first==DUCKIEBANK_REQUEST) then							-- Some request-regarding action
		elseif (first==DUCKIEBANK_SETTING) then
		elseif (first==DUCKIEBANK_MARKER1 or first==DUCKIEBANK_MARKER2 or first==DUCKIEBANK_MARKER3 or first==DUCKIEBANK_MARKER4 or
				first==DUCKIEBANK_MARKER5 or first==DUCKIEBANK_MARKER6 or first==DUCKIEBANK_MARKER7 or first==DUCKIEBANK_MARKER8 or first==DUCKIEBANK_MARKER9) then
		else
			DuckieBank_Header.Type=DB_TYPE_BANKER;
			local index=DuckieBank_BankerIndex(gotmarker,false);		-- Poll for the banker index
--			if (not index and DuckieBank_LS.NotAgressive) then			-- I don't have this banker in my base
			if (not index) then											-- I don't have this banker in my base
				local index=DuckieBank_BankerIndex(gotmarker,true);		-- Create the banker
				index.Stamp=gotstamp-10;								-- Set stamp as less than what was received
			end
		end
	end

	-- Version checker
	if (DuckieBank_LS.Debug) then
		if (versionreceived2>0) then								-- We got a sender version
			if (versionreceived2<tonumber(DUCKIEBANK_VERSION_BANKNUM)) then
				DuckieBank_Chat("Current sender has old version: "..versionreceived2);
			end
		end
		if (versionreceived1>0) then								-- We got a banker version
			if (versionreceived1<tonumber(DUCKIEBANK_VERSION_BANKNUM)) then
				DuckieBank_Chat("This bank-data has old version: "..versionreceived1);
			end
		end
	end

	if (versionreceived1<versionreceived2) then versionreceived1=versionreceived2; end;
	if (versionreceived1>0 and DuckieBank_VersionNotified~=true) then
		if (versionreceived1>tonumber(DUCKIEBANK_VERSION_BANKNUM)) then
			DuckieBank_Chat(DUCKIEBANK_NOTIFY_NEWVERSION);
			DuckieBank_VersionNotified=true;
		end
	end

	if (DuckieBank_LS.Debug) then
		if (seq==1) then DuckieBank_Chat("DEBUG: Callback_INFO called with empty table");
		elseif (nothing) then DuckieBank_Chat("DEBUG: No known info in table for Callback_INFO ("..(seq-1).." entries)"); end
		if (unkonwntag) then DuckieBank_Chat("DEBUG: First unknown tag: "..unknowntag); end
	end
end


-- input - A DuckieBank marker
function DuckieBank_Callback_CS(input)
	if (not input) then return 0; end									-- No marker

	local first,second,third,fourth=DuckNet_SplitRequestMarker(input);
	if (input==DUCKIEBANK_COLLATE) then									-- A banker wants its requests
		DuckieBank_Header.Type=DB_TYPE_REQUEST;
		return DuckieBank_CountRequestBlocks();
	elseif (first==DUCKIEBANK_REQUEST) then								-- Poll for a request
		if (not second or not third) then return 0; end
		DuckieBank_Header.Type=DB_TYPE_REQUEST;
		if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return 0; end
		local tp=DuckieBank_ValidateRequest(second,true,third);											-- Find root and create if not there
		if (not tp.Stamp) then tp.Stamp=time()-(DBT_REQUEST_DEATH+3600); end							-- Make it almost dead
		return tp.Stamp;
	elseif (first==DUCKIEBANK_SETTING) then
		if (not second) then return 0; end
		DuckieBank_Header.Type=DB_TYPE_SETTINGS;
		if (not DuckieBank_LastGuild) then DuckieBank_Chat(DUCKIEBANK_NOTIFY_NODATABASE,1); return 0; end
		local index=DuckieBank_BankerIndex(second); if (not index) then return; end;
		if (not DuckieBank_LastGuild[index].Settings) then return 0; end								-- No settings
		if (not DuckieBank_LastGuild[index].Settings.Stamp) then return 0; end							-- No stamp
		return DuckieBank_LastGuild[index].Settings.Stamp;
	elseif (first==DUCKIEBANK_MARKER1 or first==DUCKIEBANK_MARKER2 or first==DUCKIEBANK_MARKER3 or first==DUCKIEBANK_MARKER4 or first==DUCKIEBANK_MARKER5 or
			first==DUCKIEBANK_MARKER6 or first==DUCKIEBANK_MARKER7 or first==DUCKIEBANK_MARKER8 or first==DUCKIEBANK_MARKER9) then
		return 0;
	end

	-- Assume banker-poll
	DuckieBank_Header.Type=DB_TYPE_BANKER;
	local index=DuckieBank_BankerIndex(input,false);
	if (not index) then return 0; end;
	if (not index.Stamp) then return 0; end
	local itembase=DuckieBank_BankerItems(input);
	if (not itembase) then return 0; end;
	if (table.maxn(itembase)<1) then return 0; end;			-- I have no data, so don't send stamp either
	return index.Stamp;
end


-- input - Boolean. "true" if I have the newest data and must transmit then
function DuckieBank_Callback_NW(input)
	if (input~=true) then return; end
	if (DuckieBank_Header.Type==DB_TYPE_REQUEST) then DuckieBank_SendRequests();
	elseif (DuckieBank_Header.Type==DB_TYPE_SETTINGS) then DuckieBank_SendSettings();
	elseif (DuckieBank_Header.Type==DB_TYPE_BANKER) then DuckieBank_SendBank(DuckieBank_Header.Banker);
	end
end


function DuckieBank_BankerDataCopyCopy(from,to)
	DuckNet_ClearTable(to);
	local seq=1;
	while (from[seq]) do
		to[seq]={};
		to[seq].icon=from[seq].icon;
		to[seq].count=from[seq].count;
		to[seq].link=from[seq].link;
		to[seq].cost=from[seq].cost;			-- V002 compatible
		seq=seq+1;
	end
	if (DuckieBank_LS.Debug) then
		if (seq==1) then DuckieBank_Chat("DEBUG: No bank-data in table for local storage");
		else DuckieBank_Chat("DEBUG: "..(seq-1).." entries copied"); end
	end;
end


--[[    Minimap icon stuff    ]]
function DuckieBank_Minimap_OnEnter()
	GameTooltip:SetOwner(this,"ANCHOR_LEFT");
	GameTooltip:SetText(DUCKIEBANK_VERSIONTEXT);
	GameTooltipTextLeft1:SetTextColor(0,1,0);
	GameTooltip:AddLine(DUCKIEBANK_HOVER_MMLC);
	GameTooltip:AddLine(DUCKIEBANK_HOVER_MMRC);
	GameTooltip:AddLine(DUCKIEBANK_HOVER_MMSRC);

	if ((DuckieBank_Monitor or DuckieBank_LS.Debug) and DuckieBank_LastGuild) then
		GameTooltip:AddDoubleLine(" ",DUCKIEBANK_HOVER_MMMONITOR,1,1,1,1,1,1);
		local none=true;
		for banker,count in pairs(DuckieBank_NewStuff) do
			none=false;
			GameTooltip:AddDoubleLine(" ",banker..": "..count,1,1,1,1,1,1);
		end
		if (none) then GameTooltip:AddDoubleLine(" ","- "..DUCKIEBANK_HOVER_MMNONE.." -",1,1,1,1,1,1); end
	end
	GameTooltip:Show();
end

function DuckieBank_Icon_OnClick()
	DuckieBank_ToggleMainFrame();
end


-- Thanks to Yatlas and Gello for the initial code
function DuckieBank_BeingDragged()
	-- Thanks to Gello for this code
	local xpos,ypos = GetCursorPosition();
	local xmin,ymin = Minimap:GetLeft(), Minimap:GetBottom()

	if (IsShiftKeyDown()) then
		DuckieBank_LS.IconPosition=nil;
		xpos=(xpos/UIParent:GetScale()-xmin)-16;
		ypos=(ypos/UIParent:GetScale()-ymin)+16;
		DuckieBank_SetIconAbsolute(xpos,ypos);
		return;
	end
	DuckieBank_LS.IconX=nil;
	DuckieBank_LS.IconY=nil;

	xpos=xmin-xpos/UIParent:GetScale()+70
	ypos=ypos/UIParent:GetScale()-ymin-70

	DuckieBank_SetIconAngle(math.deg(math.atan2(ypos,xpos)));
end


function DuckieBank_SetIconAngle(v)
	if (v<0) then v=v+360; end
	if (v>=360) then v=v-360; end

	DuckieBank_LS.IconPosition=v;
	DuckieBank_MinimapIcon:SetPoint("TOPLEFT","Minimap","TOPLEFT",54-(78*cos(DuckieBank_LS.IconPosition)),(78*sin(DuckieBank_LS.IconPosition))-55);
	DuckieBank_MinimapIcon:Show();
end


function DuckieBank_SetIconAbsolute(x,y)
	DuckieBank_LS.IconX=x;
	DuckieBank_LS.IconY=y;

	DuckieBank_MinimapIcon:SetPoint("TOPLEFT","Minimap","BOTTOMLEFT",x,y);
end
